Fl4m3Ph03n1x
Mox fails test that should pass
Background
I am trying to use Mox to replicate some simple interactions but I am failing test that should be passing (or so I think):
Code
After creating a basic hello world project I modified to the following:
defmodule Testmox do
alias MyApp.Calculator.Impl, as: Calc
def hello, do: "Hello #{inspect Calc.add(1, 1)}"
def bye, do: "Bye #{inspect Calc.mult(2, 2)}"
end
Textmox uses a basic calculator module:
defmodule MyApp.Calculator do
@callback add(integer(), integer()) :: integer()
@callback mult(integer(), integer()) :: integer()
end
defmodule MyApp.Calculator.Impl do
@behaviour MyApp.Calculator
alias MyApp.Calculator
@impl Calculator
def add(x, y), do: x + y
@impl Calculator
def mult(x, y), do: x * y
end
As you see, nothing extraordinary here. Now onto the test!
defmodule CalculatorTest do
use ExUnit.Case, async: true
alias Testmox
alias MyApp.Calculator.Impl, as: Calc
import Mox
# Make sure mocks are verified when the test exits
setup :set_mox_global
setup :verify_on_exit!
test "invokes add and mult" do
MyApp.CalcMock
|> expect(:add, &Calc.add/2)
|> expect(:mult, &Calc.mult/2)
Testmox.hello()
Testmox.bye()
end
end
Problem?
This test fails, even though Calc.add and Calc.mult are being called in the hello and bye functions respectively. I have even set the mode to global, so other processes can use it, but it still fails the tests. What am I doing wrong?
EDIT: I have also changed test_helpers.ex:
ExUnit.start()
Mox.defmock(MyApp.CalcMock, for: MyApp.Calculator)
Marked As Solved
LostKobrakai
You don’t need to create an empty module. defmock will take care of creating the module, you just need to give it a name for it to use.
Also Liked
peerreynders
Going by the documentation it seems to fit the xUnit classification of Configurable Test Double.
LostKobrakai
Testmox.hello calls MyApp.Calculator.Impl.add not MyApp.CalcMock.add. In elixir there nothing, which would allow a library to change code, so Mox cannot dynamically alter what get’s called, so you need to find a way to make the implementation changeable on your own. The options for that are using the app env to configure the implementation, passing the implementation as argument to the function or some compile time magic if you don’t need to change the implementation at runtime.
The other problem is that you’re running the tests before running defmock in your test_helpers.exs.
Fl4m3Ph03n1x
So, this should work, right?
defmodule CalculatorTest do
use ExUnit.Case, async: true
alias Testmox
alias MyApp.Calculator.Impl, as: Calc
import Mox
# Make sure mocks are verified when the test exits
setup :set_mox_global
setup :verify_on_exit!
test "invokes add and mult" do
Mox.defmock(Calc, for: MyApp.Calculator)
expect(Calc, :add, &Calc.add/2)
Testmox.hello()
end
end
It doesn’t…
LostKobrakai
I still don’t see any code to make Testmox.hello call the module defined by Mox instead of calling the hardcoded implementation of MyApp.Calculator.Impl.
LostKobrakai
I think you don’t really get how Mox works yet. It doesn’t matter what you use as callback in expect() for the issues you’re having. Testmox.hello is hardcoded to call MyApp.Calculator.Impl.add, which means it’s never going to not call your actual implementation, which you probably don’t want to happen as you’re trying to mock it.
What Mox.defmock(Calc, for: MyApp.Calculator) is doing is creating a new module with the name you supply as the first argument. Mox can then assert if the functions on this new module are actually called and let it return the values by the callbacks you supply via expect. It does not somehow magically instrument existing implementations to do different things than before.
You last implemenation is does basically override your existing module with a new module, which is not what you want to do.








