Imagine simple situation: you need to test some of your code modules, but you application is made in the best qualities and traditions of OTP framework. When your tests start, your application starts also, and supervision tree starts also, and everithing is running in the same environment with your tests!

Thus, you get a banch of problems:

  • Your dev and test environments mix together;

  • You can’t test processes, that are registered with names, because they are alrady started by your supervision tree;

  • You can’t perform unit testing, because you are not realy sure, that this testing would be really unit.

The solutuion is simple: Divide and conqure!. At the beginnig - let’s turn of this annoying supervision tree!

Configure your mix file.

The first thing is simple: dont start your application in test. Fortunatly, mix test has special parameter - --no-start, which prevent any applications from starting in your test environment. Of course, we, developers, are so lazy - we don’t want to put this parameter every time when we are running tests. For more, imagine, that your team has a new member, who download the source, types mix test, and…​ get overkilled with tonns of error logs. Not the funnies start on a new place, eh?

So, let’s modify aliases a bit. Change your mix.exs file in such a way:

mix.exs
def project do
  [
    app: :my_app,
    version: "0.1.0",
    elixir: "~> 1.3",
    build_embedded: Mix.env == :prod,
    start_permanent: Mix.env == :prod,
    description: description(),
    package: package(),
    aliases: aliases(), #(1)
    deps: deps()
  ]
end
...
defp aliases do
  [
    test: "test --no-start" #(2)
  ]
end
1 Here we adding new attribute - aliases - they are simple mix tasks. you can reed more here.
2 Here we define your new alias. Change test to test --no-start, so when we run mix test it opens into mix test --no-start.

Configuring your ExUnit.

Ok, now all applications are not started. But the question is:

Is that what you need?

Not only your application wasn’t started. You environment in working without :logger, :kernel, :ecto, and dosen applications, which are automaticly started before your application. So…​ what to do?

The answer is: start them! The best place to do this - is before calling ExUnit.start in you test_helper.exs file. We can do this manualy, if we want total controll:

/test/test_helper.exs
Application.ensure_all_started(:ecto) #(1)

ExUnit.start() #(2)
1 Here we manualy start :ecto application with all it’s dependencies. You can read more about this function here.
2 This line is already in your autogenerated test_helper.exs file.

But this is the solution, how to start all dependencis of your application without starting your application:

/test/test_helper.exs
for app <- Application.spec(:my_app,:applications) do #(1)
  Application.ensure_all_started(app)
end

ExUnit.start()
1 List all the applications, which are dependencies of yours. Then just ensure, that they are all started. You can read more about spec function here.

Of cource, if you will modify your dependencies, second solution will track this changes, in comparison with first, which forses you to controll everything by yourself. In all cases, now you see, that you can write your user code to control what to start in your test cases.

comments powered by Disqus