0
votes

I’m working with elixir and am seeing confusing/undesirable behavior when using Mox.

I just want to use a mock for a single test module. Let’s say I have 2 tests. Here is sample code:

defmodule MyTest do
  setup_all do
    defmock(DateTimeMock, for: DateTimeApi)

    :ok
  end

  test "test1" do
    {:ok, expected_datetime, _} = DateTime.from_iso8601("2019-09-08T00:00:00.000000Z")
    expect(DateTimeMock, :utc_now, fn _ -> expected_datetime end)
  end

  test "test2" do
    expect(something else)
  end
end 

defmodule MyTest2 do
  setup_all do
    defmock(DateTimeMock, for: DateTimeApi)

    :ok
  end

  test "test1" do
  end

  test "test2" do
  end
end 

When MyTest2 runs I will see the error: (Mox.UnexpectedCallError) no expectation defined

Defining a mock for a single test ‘leaks’ out and affects all tests.

Does Mox have a way to revert the mocked module back to the original module after the test has finished?

1
This question looks identical to elixirforum.com/t/resetting-mox-mock-after-a-single-test/26435. Are you unsatisfied with the answers given there?Dogbert
looking for fresh air and new ideaskemilljeans

1 Answers

0
votes

You can make use of the on_exit callback in the setup block. The main part of the setup function runs before each test (so it reads the current value), then the on_exit callback runs after each test (so it can set things back to their original values).

defmodule MyTest do
  use ExUnit.Case

  setup do
    date_time_api = Application.get_env(:my_app, :date_time_api)
    on_exit(
      fn ->
        Application.put_env(:my_app, :date_time_api, date_time_api)
      end
    )
  end

  # Tests, fixtures follow...
end

Remember that it is important that your code defers to the run-time configuration available in the Application, so your code might look something like this:

def utc_now do
  Application.get_env(:my_app, :date_time_api).utc_now()
end

With a corresponding config value of something like

# config/config.exs or env-specific config file
config :my_app, date_time_api: DateTimeApi

That's what makes the whole thing work because the app will resolve the module name (i.e. the real one or the mock one) at run-time.