ExUnit.Callbacks — ExUnit v1.16.3

Search documentation of ExUnit

# View Source ExUnit.Callbacks (ExUnit v1.16.3)

Defines ExUnit callbacks.

This module defines the `setup/1`, `setup/2`, `setup_all/1`, and `setup_all/2` callbacks, as well as the `on_exit/2`, `start_supervised/2` and `stop_supervised/1` functions.

The setup callbacks may be used to define test fixtures and run any initialization code which help bring the system into a known state. They are defined via macros and each one can optionally receive a map with test state and metadata, usually referred to as the `context`. Optionally, the context to be used in the tests can be extended by the setup callbacks by returning a properly structured value (see below).

The `setup_all` callbacks are invoked only once per module, before any test is run. All `setup` callbacks are run before each test. No callback is run if the test case has no tests or all tests have been filtered out.

`setup` and `setup_all` callbacks can be defined by either a block, an atom naming a local function, a `{module, function}` tuple, or a list of atoms/tuples.

Both can opt to receive the current context by specifying it as parameter if defined by a block. Functions used to define a test setup must accept the context as single argument.

A test module can define multiple `setup` and `setup_all` callbacks, and they are invoked in order of appearance.

`start_supervised/2` is used to start processes under a supervisor. The supervisor is linked to the current test process. The supervisor as well as all child processes are guaranteed to terminate before any `on_exit/2` callback runs.

`on_exit/2` callbacks are registered on demand, usually to undo an action performed by a setup callback. `on_exit/2` may also take a reference, allowing the callback to be overridden in the future. A registered `on_exit/2` callback will always run, while failures in `setup` and `setup_all` will stop all remaining setup callbacks from executing.

Finally, `setup_all` callbacks run in a separate process per module, while all `setup` callbacks run in the same process as the test itself. `on_exit/2` callbacks always run in a separate process, as implied by their name. The test process always exits with reason `:shutdown`, which means any process linked to the test process will also exit, although asynchronously. Therefore it is preferred to use `start_supervised/2` to guarantee synchronous termination.

Here is a rundown of the life-cycle of the test process:

1. the test process is spawned
2. it runs `setup/2` callbacks
3. it runs the test itself
4. it stops all supervised processes
5. the test process exits with reason `:shutdown`
6. `on_exit/2` callbacks are executed in a separate process

## Context

If `setup_all` or `setup` return a keyword list, a map, or a tuple in the shape of `{:ok, keyword() | map()}`, the keyword list or map will be merged into the current context and will be available in all subsequent `setup_all`, `setup`, and the `test` itself.

Returning `:ok` leaves the context unchanged (in `setup` and `setup_all` callbacks).

Returning anything else from `setup_all` will force all tests to fail, while a bad response from `setup` causes the current test to fail.

## Examples

    defmodule AssertionTest do
      use ExUnit.Case, async: true

      # "setup_all" is called once per module before any test runs
      setup_all do
        IO.puts("Starting AssertionTest")

        # Context is not updated here
        :ok
      end

      # "setup" is called before each test
      setup do
        IO.puts("This is a setup callback for #{inspect(self())}")

        on_exit(fn ->
          IO.puts("This is invoked once the test is done. Process: #{inspect(self())}")
        end)

        # Returns extra metadata to be merged into context.
        # Any of the following would also work:
        #
        #     {:ok, %{hello: "world"}}
        #     {:ok, [hello: "world"]}
        #     %{hello: "world"}
        #
        [hello: "world"]
      end

      # Same as above, but receives the context as argument
      setup context do
        IO.puts("Setting up: #{context.test}")

        # We can simply return :ok when we don't want to add any extra metadata
        :ok
      end

      # Setups can also invoke a local or imported function that returns a context
      setup :invoke_local_or_imported_function

      test "always pass" do
        assert true
      end

      test "uses metadata from setup", context do
        assert context[:hello] == "world"
        assert context[:from_named_setup] == true
      end

      defp invoke_local_or_imported_function(context) do
        [from_named_setup: true]
      end
    end

It is also common to define your setup as a series of functions, which are put together by calling `setup` or `setup_all` with a list of function names. Each of these functions receive the context and can return any of the values allowed in `setup` blocks:

    defmodule ExampleContextTest do
      use ExUnit.Case

      setup [:step1, :step2, :step3, {OtherModule, :step4}]

      defp step1(_context), do: [step_one: true]
      defp step2(_context), do: {:ok, step_two: true} # return values with shape of {:ok, keyword() | map()} allowed
      defp step3(_context), do: :ok # Context not modified

      test "context was modified", context do
        assert context[:step_one] == true
        assert context[:step_two] == true
      end
    end

Finally, as discussed in the `ExUnit.Case` documentation, remember that the initial context metadata can also be set via `@tag`s, which can then be accessed in the `setup` block:

    defmodule ExampleTagModificationTest do
      use ExUnit.Case

      setup %{login_as: username} do
        {:ok, current_user: username}
      end

      @tag login_as: "max"
      test "tags modify context", context do
        assert context[:login_as] == "max"
        assert context[:current_user] == "max"
      end
    end

# Summary

## Functions

on_exit(name_or_ref \\ make_ref(), callback)

Registers a callback that runs once the test exits.

setup(block_or_functions)

Defines a callback to be run before each test in a case.

setup(context, block)

Defines a callback to be run before each test in a case.

setup_all(block)

Defines a callback to be run before all tests in a case.

setup_all(context, block)

Defines a callback to be run before all tests in a case.

start_link_supervised!(child_spec_or_module, opts \\ [])

Same as `start_supervised!/2` but links the started process to the test process.

start_supervised(child_spec_or_module, opts \\ [])

Starts a child process under the test supervisor.

start_supervised!(child_spec_or_module, opts \\ [])

Same as `start_supervised/2` but returns the PID on success and raises if not started properly.

stop_supervised(id)

Stops a child process started via `start_supervised/2`.

stop_supervised!(id)

Same as `stop_supervised/1` but raises if it cannot be stopped.

# Functions

Link to this function

# on_exit(name_or_ref \\ make_ref(), callback)

View Source

    @spec on_exit(term(), (-> term())) :: :ok

Registers a callback that runs once the test exits.

`callback` is a function that receives no arguments and runs in a separate process than the caller. Its return value is irrelevant and is discarded.

`on_exit/2` is usually called from `setup/1` and `setup_all/1` callbacks, often to undo the action performed during the setup.

However, `on_exit/2` may also be called dynamically. An "ID" (the `name_or_ref` argument) can be used to guarantee that the callback will be invoked only once. ExUnit uses this term to identify an `on_exit/2` handler: if you want to override a previous handler, for example, use the same `name_or_ref` across multiple `on_exit/2` calls.

If `on_exit/2` is called inside `setup/1` or inside a test, it's executed in a blocking fashion after the test exits and before running the next test. This means that no other test from the same test case will be running while the `on_exit/2` callback for a previous test is running. `on_exit/2` is executed in a different process than the test process. On the other hand, if `on_exit/2` is called inside a `setup_all/1` callback then `callback` is executed after running all tests (see `setup_all/1` for more information).

## Examples

    setup do
      File.write!("fixture.json", "{}")
      on_exit(fn -> File.rm!("fixture.json") end)
    end

You can use the same `name_or_ref` across multiple `on_exit/2` calls to "override" the registered handler:

    setup do
      on_exit(:drop_table, fn ->
        Database.drop_table()
      end)
    end

    test "a test that shouldn't drop the table" do
      on_exit(:drop_table, fn -> :ok end)
    end

Relying too much on overriding callbacks like this can lead to test cases that are hard to understand and with too many layers of indirection. However, it can be useful in some cases or for library authors, for example.

Link to this macro

# setup(block_or_functions)

View Source (macro)

Defines a callback to be run before each test in a case.

Accepts one of these:

- a block
- an atom naming a local function
- a `{module, function}` tuple
- a list of atoms and `{module, function}` tuples

Can return values to be merged into the context, to set up the state for tests. For more details, see the "Context" section shown above.

`setup/1` callbacks are executed in the same process as the test process.

## Examples

    defp clean_up_tmp_directory(context) do
      # perform setup
      :ok
    end

    setup :clean_up_tmp_directory

    setup [:clean_up_tmp_directory, :another_setup]

    setup do
      [conn: Plug.Conn.build_conn()]
    end

    setup {MyModule, :my_setup_function}

Link to this macro

# setup(context, block)

View Source (macro)

Defines a callback to be run before each test in a case.

This is similar to `setup/1`, but the first argument is the context. The `block` argument can only be a block.

For more details, see the "Context" section shown above.

## Examples

    setup context do
      [conn: Plug.Conn.build_conn()]
    end

Link to this macro

# setup_all(block)

View Source (macro)

Defines a callback to be run before all tests in a case.

Accepts one of these:

- a block
- an atom naming a local function
- a `{module, function}` tuple
- a list of atoms and `{module, function}` tuples

Can return values to be merged into the `context`, to set up the state for tests. For more details, see the "Context" section shown above.

`setup_all/1` callbacks are executed in a separate process than tests. All `setup_all/1` callbacks are executed in order in the same process.

## On-Exit Handlers

On-exit handlers that you register inside `setup_all/1` callbacks are executed at once after all tests in the module have been run. They are all executed in the same process, which is a separate process dedicated to running these handlers. These handlers are executed in the reverse order of their respective `setup_all/1` callbacks.

## Examples

    # One-arity function name
    setup_all :clean_up_tmp_directory

    # A module and function
    setup_all {MyModule, :my_setup_function}

    # A list of one-arity functions and module/function tuples
    setup_all [:clean_up_tmp_directory, {MyModule, :my_setup_function}]

    defp clean_up_tmp_directory(_context) do
      # perform setup
      :ok
    end

    # A block
    setup_all do
      [conn: Plug.Conn.build_conn()]
    end

The context returned by `setup_all/1` will be available in all subsequent `setup_all`, `setup`, and the `test` itself. For instance, the `conn` from the previous example can be accessed as:

    test "fetches current users", %{conn: conn} do
      # ...
    end

### Handlers

You can define "global" on-exit handlers in `setup_all/1` callbacks:

    setup_all do
      Database.create_table_for(__MODULE__)

      on_exit(fn ->
        Database.drop_table_for(__MODULE__)
      end)

      :ok
    end

The handler in the example above will be executed only once, after running all tests in the module.

Link to this macro

# setup_all(context, block)

View Source (macro)

Defines a callback to be run before all tests in a case.

Similar as `setup_all/1` but also takes a context. The second argument must be a block. See the "Context" section in the module documentation.

## Examples

    setup_all _context do
      [conn: Plug.Conn.build_conn()]
    end

Link to this function

# start_link_supervised!(child_spec_or_module, opts \\ [])

View Source (since 1.14.0)

    @spec start_link_supervised!(
      Supervisor.child_spec() | module() | {module(), term()},
      keyword()
    ) :: pid()

Same as `start_supervised!/2` but links the started process to the test process.

If the process that was started crashes, the crash is propagated to the test process, failing the test and printing the cause of the crash.

Note that if the started process terminates before it is linked to the test process, this function will exit with reason `:noproc`.

Link to this function

# start_supervised(child_spec_or_module, opts \\ [])

View Source (since 1.5.0)

    @spec start_supervised(
      Supervisor.child_spec() | module() | {module(), term()},
      keyword()
    ) :: Supervisor.on_start_child()

Starts a child process under the test supervisor.

It expects a child specification or a module, similar to the ones given to `Supervisor.start_link/2`. For example, if your application starts a supervision tree by running:

    Supervisor.start_link([MyServer, {OtherSupervisor, ...}], ...)

You can start those processes under test in isolation by running:

    start_supervised(MyServer)
    start_supervised({OtherSupervisor, :initial_value})

A keyword list can also be given if there is a need to change the child specification for the given child process:

    start_supervised({MyServer, :initial_value}, restart: :temporary)

See the `Supervisor` module for a discussion on child specifications and the available specification keys.

The advantage of starting a process under the test supervisor is that it is guaranteed to exit before the next test starts. Therefore, you don't need to remove the process at the end of your tests via `stop_supervised/1`. You only need to use `stop_supervised/1` if you want to remove a process from the supervision tree in the middle of a test, as simply shutting down the process would cause it to be restarted according to its `:restart` value.

The started process is not linked to the test process and a crash will not necessarily fail the test. To start and link a process to guarantee that any crash would also fail the test use `start_link_supervised!/2`.

This function returns `{:ok, pid}` in case of success, otherwise it returns `{:error, reason}`.

Link to this function

# start_supervised!(child_spec_or_module, opts \\ [])

View Source (since 1.6.0)

    @spec start_supervised!(
      Supervisor.child_spec() | module() | {module(), term()},
      keyword()
    ) :: pid()

Same as `start_supervised/2` but returns the PID on success and raises if not started properly.

Link to this function

# stop_supervised(id)

View Source (since 1.5.0)

    @spec stop_supervised(id :: term()) :: :ok | {:error, :not_found}

Stops a child process started via `start_supervised/2`.

This function expects the `id` in the child specification. For example:

    {:ok, _} = start_supervised(MyServer)
    :ok = stop_supervised(MyServer)

It returns `:ok` if there is a supervised process with such `id`, `{:error, :not_found}` otherwise.

Link to this function

# stop_supervised!(id)

View Source (since 1.10.0)

    @spec stop_supervised!(id :: term()) :: :ok

Same as `stop_supervised/1` but raises if it cannot be stopped.