jrhite
Resetting Mox mock after a single test
I’m new to 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?
Most Liked
LostKobrakai
There are a few things. Mock modules of Mox are empty shells. Without expectations/stubs they don’t know what to do/return when being called. Therefore there’s a technical requirement to setting up expectations/stubs.
The other part is that you don’t want to have tests, which do not tell you about the usage of a mock. Without expectations/stubs being setup in a testcase any other developer might think the same code is run as is in production, which is not true.
If you don’t want to have to setup stubs/expectations for your mock “everywhere” the best solution is to not use the mock for those testcases, but e.g. a module with a dummy implementation or even the prod implementation.
Aside:
You should put your defmock calls in test_helpers.exs and not in your setup functions. defmock creates a module and modules are always global to the beam instance. You cannot scope those to a single test unless you use different names.
al2o3cr
Mox doesn’t have this feature because that’s not how it works - somewhere in your test setup / config, code is storing DateTimeMock in a spot where production stores DateTimeApi. The docs show two possible forms, but your code may do something different:
config :my_app, :calculator, MyApp.CalcMock
# or
Application.put_env(:my_app, :calculator, MyApp.CalcMock)
What you’re describing is closer to the behavior of tools like :meck which manipulate module resolution at runtime. That comes with a whole new set of weird behaviors (for instance, code coverage can fail unexpectedly).
jrhite
I see, thanks for the explanation.
I think the best solution in my case would be to create the Mox mock in test_helper and then also use stub_with using the real implementation for tests that haven’t set expectations yet. Then the team can change the tests over on a test-by-test basis.







