ExUnitFixtures v0.2.0 ExUnitFixtures

A library for declaring & using test fixtures in ExUnit.

For an overview of it’s purpose see the README.

To use ExUnitFixtures, you should use ExUnitFixtures in your test case (before use ExUnit.Case), and then define your fixtures using deffixture/3. These fixtures can then be used by tagging your tests with the fixtures tag. For example:

iex(2)> defmodule MyTests do
...(2)>   use ExUnitFixtures
...(2)>   use ExUnit.Case
...(2)>
...(2)>   deffixture my_model do
...(2)>     # Create a model somehow...
...(2)>     %{test: 1}
...(2)>   end
...(2)>
...(2)>   @tag fixtures: [:my_model]
...(2)>   test "that we have some fixtures", context do
...(2)>     assert context.my_model.test == 1
...(2)>   end
...(2)> end
iex(3)> Module.defines?(MyTests, :create_my_model)
true

Fixtures with dependencies

Fixtures can also depend on other fixtures by naming a parameter after that fixture. For example, if you needed to setup a database instance before creating some models:

iex(4)> defmodule MyTests2 do
...(4)>   use ExUnitFixtures
...(4)>   use ExUnit.Case
...(4)>
...(4)>   deffixture database do
...(4)>     # set up the database somehow...
...(4)>   end
...(4)>
...(4)>   deffixture my_model(database) do
...(4)>     # use the database to insert a model
...(4)>   end
...(4)>
...(4)>   @tag fixtures: [:my_model]
...(4)>   test "something", %{my_model: my_model} do
...(4)>     # Test something with my_model
...(4)>   end
...(4)> end
iex(5)> Module.defines?(MyTests2, :create_database)
true
iex(6)> Module.defines?(MyTests2, :create_my_model)
true

In the sample above, we have 2 fixtures: one which creates the database and another which inserts a model into that database. The test function depends on my_model which depends on the database. ExUnitFixtures knows this, and takes care of setting up the database and passing it in to my_model.

Fixture Scoping

Fixtures may optionally be provided with a scope:

  • :test scoped fixtures will be created before a test and deleted afterwards. This is the default scope for a fixture.
  • :module scoped fixtures will be created at the start of a test module and passed to every single test in the module.

For details on how to specify scopes, see deffixture/3.

Tearing down Fixtures

If you need to do some teardown work for a fixture you can use the ExUnit on_exit function:

iex(8)> defmodule TestWithTearDowns do
...(8)>   use ExUnitFixtures
...(8)>   use ExUnit.Case
...(8)>
...(8)>   deffixture database do
...(8)>     # Setup the database
...(8)>     on_exit fn ->
...(8)>       # Tear down the database
...(8)>       nil
...(8)>     end
...(8)>   end
...(8)> end
iex(9)> Module.defines?(MyTests2, :create_database)
true

Sharing Fixtures Amongst Test Cases.

Fixtures are fully compatible with ExUnit.CaseTemplate - this is the current recommended way to share some fixtures among a number of files. Simply defined your fixtures in an ExUnit.CaseTemplate, and then any file that imports that CaseTemplate with use will have access to all the fixtures defined within. For example:

defmodule MyFixtures do
  use ExUnitFixtures
  use ExUnit.CaseTemplate

  deffixture database do
    # Setup the database.
    %{database: :db}
  end
end

def Tests do
  use MyFixtures
  use ExUnit.Case

  @tag fixtures: [:database]
  test "that we have a database", %{database: db} do
    assert db == :db
  end
end

Currently a test case can’t use both fixtures imported from a CaseTemplate and locally defined fixtures, though there are plans to add support for that in the future.

Note: The example above will work as is if both modules are defined in the same file. However, you’ll need to do some work to ensure your CaseTemplate is loaded when your tests are running. You can do this using the Code.load_file function in your test_helper.exs file, as described in this stack overflow answer.

Summary

Macros

Defines a fixture local to a test module

Macros

deffixture(arg, opts \\ [], body)

Defines a fixture local to a test module.

This is intended to be used much like a def statement:

deffixture my_fixture do
  "my_fixture_text"
end

A fixture may optionally depend on other fixtures. This is done by creating a fixture that accepts parameters named after other fixtures. These fixtures will automatically be run and injected as parameters to the current fixture. For example:

deffixture database do
  %{database: true}
end

deffixture model(database) do
  %{model: true}
end

Fixture Options

Fixtures can accept various options that control how they are defined:

deffixture database, scope: :module do
  %{database: true}
end

These options are supported:

  • scope controls the scope of fixtures. See Fixture Scoping for details.
  • Passing autouse: true will cause a fixture to be passed to every test in the module.