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 everything is running in the same environment with your tests!
Thus, you get a bench of problems:
- Your dev and test environments mix together;
- You can’t test processes, that are registered with names, because they are already started by your supervision tree;
- You can’t perform unit testing, because you are not really sure, that this testing would be really unit.
The solution is simple: Divide and conquer!
At the beginning - let’s turn of this annoying supervision tree!
Configure your mix file
The first thing is simple: dont start your application in test. Fortunately,
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 tonnes 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:
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
- Here we adding new attribute - aliases - they are simple mix tasks. you can reed more here.
- Here we define your new alias. Change
test --no-start, so when we run
mix testit 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
:ecto, and dozen applications, which are automatically 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 manually, if we want total control:
Application.ensure_all_started(:ecto) #(1) ExUnit.start() #(2)
- Here we manually start
:ectoapplication with all it’s dependencies. You can read more about this function here.
- This line is already in your autogenerated
But this is the solution, how to start all dependencies of your application without starting your application:
Application.load(:my_app) #(1) for app <- Application.spec(:my_app,:applications) do #(2) Application.ensure_all_started(app) end ExUnit.start()
- We should load application first, to get access to it’s specification.
- List all the applications, which are dependencies of yours. Then just ensure, that they are all started. You can read more about
Of course, if you will modify your dependencies, second solution will track this changes, in comparison with first, which forces you to control 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.
Thanks amberbit for good comment about