Mutation testing for Elixir.
The package is available in Hex.
It can be installed by adding darwin to your list of dependencies in mix.exs:
def deps do
[
{:darwin, "~> 0.1.0"}
]
endDocumentation is on ExDoc and published on HexDocs. The docs can be found at https://hexdocs.pm/darwin.
Darwin requires few changes to your workflow. The only required changes are:
- Add the list of modules to mutate to your mix project
- Only run
ExUnit.start()in yourtest/test_helpers.exsfile if Darwin isn't running - Add
use Darwinto all your modules containing test cases
You can add modules for Darwin to mutate to the :darwin key in your mix project.
the :modules key expects a list of pairs in which the first element is a module name
and the second element is a keyword list containing options and their values
(by default the empty list).
defmodule MyApp.MixProject do
use Mix.Project
def project do
[
# ...
darwin: [
# This will be a list of modules and their respective options.
# Currently, no options are recognized, but the format is kept
# to accomodate future changes
# (for example, different test timeouts per module)
modules: [
{ModuleToMutate, []},
{AnotherModuleToMutate, []}
]
]
]
end
# ...
endThe function call ExUnit.start() does much more than just starting the :ex_unit app, which can be a problem for tools like Darwin, which want to take care of the way the tests are run.
In that case, ExUnit.start() only gets in the way, and things can go much smoother if Darwin starts ExUnit by itself.
You should change this line in your tests/test_helper.exs:
ExUnit.start()Into the following:
unless Darwin.running?() do
ExUnit.start()
endThat way, if you want to run "normal" tests using mix test, the :ex_unit application will be started normally.
If you want to run the mutation tests, then Darwin will start the :ex_unit application iself.
Add the use Darwin.TestCase directive in all your modules containing test cases.
That way, the module
defmodule MyTest do
use ExUnit.Case, async: true
# ...
endBecomes
defmodule MyTest do
use ExUnit.Case, async: true
use Darwin.TestCase
# ...
endIf you don't add the use Darwin.TestCase, the tests will work normally, but you lose the ability to stop the test suite as soon as a single test fails. Because you'll be running the test suite hundreds of times (once per mutation), this can save an enormous amount of time. This is even more important if mutations in your code cause it to enter infinite loops, which are only recognized as failing tests after a timeout.
The use Darwin.TestCase directive actually overwrites the ExUnit.Case.test/2 and ExUnit.Case.test/3 macros, so that they won't execute after the first test fails. The test setup will be run, though (and some test setups can be quite lengthy...). The solution is to somehow add support for skipping all tests (and test setups) after the first failure in the entire suite.
Darwin will log to the console mutants that survive (in red) or are killed (in green). The goal for the test suite is to kill as many mutants as possible.
The main output of darwin comes from the HTML reporter, though.
The reporter outputs its files into the darwin/reports/html/ directory.
The reporter output is still under heavy development.