Faster test execution in Elixir

Software testing is probably part of every project, regardless of its size. When the project is small, the execution of all tests takes only a moment. However, as the project size and the number of tests increases, the time required to complete all tests can increase significantly.

A lot depends on the characteristics of the tests. In the case of unit tests, their execution should be relatively quick. The more complex and multi-layered / functional tests, the slower the execution becomes.

Testing is essential to make sure nothing is damaged. You can test everything manually, but you’d rather not check each path manually. Especially if they are repeatable tests. In this case, it is much better to use automatic tests.

The setup1 vs setup_all2 functions

Let’s start with the setup and setup_all callbacks. Their task is simple - prepare the system state for test execution. In the case of setup_all, we get one call per module. It takes place before running the tests. The setup is run before each test.

For many tests, preparing the data once instead of preparing the data repeatedly, in the same way, can speed up the process. It is possible, especially if we do not modify the data, affecting other tested modules. Whenever you can, instead of setup, use the setup_all version to prepare the data once and then use it.

It is worth noting that it is possible to use more than one callback in a given module. They will be invoked one by one, as they appear in the module.

Remember about the separation of processes in which these callbacks are performed. The setup is run in the same process as the test itself. In the case of setup_all, this is a separate, independent process per module.

async: true3

The second way to speed up the execution of your tests is to make them more concurrent. It allows you to run concurrent tests with other tests outside the given module. Tests in the same module never run concurrently.

partitions4

We can execute the tests asynchronously, but this is not always enough acceleration. It may even be impossible in the case of tests based on global resources or making changes to the database.

Most of the projects have continuous integration processes prepared that allow you to test every change in the code. We can use the --partitions flag to define how many test partitions that particular instance is running. It can also be helpful if you want to distribute testing across multiple machines. Suppose mix test --partitions 3 is specified. In that case, the tests will be arranged and allocated round-robin to one of the three groups, which are then executed independently.

It is a huge acceleration for testing. If something takes several minutes locally, it can be performed in just a few minutes with several groups of tests.

tags5 and filters6

Tags allow you to enter context for test execution. You can use @tag key: value to match the nearest test or @moduletag or @describetag inside each context, respectively. This way, you can indicate that a given test takes a long time, concerns integration with an external API, or you want to ignore it (for example, due to race conditions).

Filtering allows you to select or exclude specific tests from running. It may be necessary to, for example, run integration tests with external systems only in a previously prepared environment instead of each time on the developer’s local machine.

We can use the --include option to select a group or the --exclude option to exclude such a tag from execution. It is also possible to use --only to select only one tag group.

Summary

By using the options provided by ExUnit, you can significantly speed up your tests. The best results can be achieved by using all the indicated methods at the same time. For tests that can share data, setup_all will be an excellent solution for preparing data for tests.

Combining --partitions and tags (--include / --exclude) will allow you to test solutions even faster. In the beginning, performing quick tests that will verify the overall correctness of the system operation. If they are successful, more time-consuming tests can be performed. You can discover these by calling mix test --slowest num, which will give you a list of num tests that took the longest to execute.

Successfully accelerate your tests and make your project work more enjoyable!

Get new posts and extra comments

You'll receive every new post with extra unpublished comments available only to the subscribers!

I won't send you spam. Unsubscribe at any time.