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.
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance








