After studying Elixir for a while and understanding how it works, I came across some questions about how writing tests would be in a functional language and where to start.
To begin with, Elixir comes with ExUnit, a test framework that provides pretty much everything we need to test our code.
How to use it?
Everytime we start a project with
mix new project_name we can see the tests folder in its output:
If we open the
hello_world_test.exs file, we will find our test’s basic structure.
Right at the beginning, we can see that there is a line
use ExUnit.case, ExUnit.case is a module that we should use in other modules so we can configure and prepare them to be tested.
The test itself is pretty simple and self-explained, but it already gives us an idea of the structure we can use to write our tests.
There are some
asserts that can help us to execute our tests. The most common is
Assert to compare something it is expected to be true and
refute to compare something that is expected to be false.
assert to_string(:foo) == "foo"
assert_in_delta 3.141, 3.142, 0.001
assert_raise ArithmeticError, fn -> 1 + "test" end
assert_receive :down, 500
assert_received :hello, "Hi!"
refute 1+1, 3
refute_in_delta 3.141, 3.142, 0.001
asserts are pretty much similar to most framework tests, the highlight here is for
They help us to test async code, for example, you have a
Task that executes some code and you wish to monitor the process and wait for some message. In this scenario,
assert_receive would help us to test that we received the message.
In the above test, we are asserting that the Task exit is
:normal and that it executed in up to 500 milliseconds.
There are a few more
asserts that may help us and you can find them at the ExUnit official documentation.
To group our tests, there is a function called
describe and it is a very used convention to organize the tests in a group of examples by function.
Using Mix, you can run only one block of
describe if you want to:
$ mix test --only describe:"Some.Function"
In some cases, it may be necessary to do some kind of
setup of the
system under test before running our tests. For this, there are two
macros that can help us, the
The difference between them is that
setup is executed every time before a test and
setup_all is executed just one time before the
Running your test suit
There are a few ways we can run our test suit, to run the whole test suit we can use the command
$ mix test
We can execute only one file, we just need to pass the file path to the command
$ mix test path/to/file.exs.
And also, we can execute only one test of the file, passing the test line as argument
$ mix test path/to/file.exs:line
Besides these options, you can also use a
tag to mark your test and use it, either to skip it or run only it, according to what you need.
To run only the tests marked as
$ mix test --only wip
Or, to exclude them:
$ mix test --exclude wip
If necessary, we can also configure our test suit to exclude tests with certain tags, to do it so we need to alter our
test_helper.exs and add a config.
So when we run our test suit, all the tests with the
wip tag will be ignored.
Mocks and Stubs
The truth is, if you’re used to writing tests in your day-to-day life, there’s a good chance you’ve used mocks and stubs to handle contracts or simulate behaviors.
Some testing frameworks such as Ruby’s
RSpec already come with options for mocks and stubs.
ExUnit, however, does not come with anything for that, in this post José Valim explains why the use of mocks can be harmful to the design of your application and how to avoid it in Elixir.
In short terms, all Elixir applications come with standard configuration files and we can often use them to configure how a particular dependency will behave in different environments and use it in our tests, removing the need to use mock.
But sometimes this will not be enough, and if you still need to use mocks or stubs, there are some frameworks like Meck that can help you.