Unrelated tests fail because "no expectation defined" when using Mox library, and stub_with/2
doesn't seem to be of any help
There is the Recaptcha library
https://github.com/samueljseay/recaptcha
which helps me in verifying recaptcha responses. All nice. Time for testing (yeah, after getting code to work somehow – apologies to all TDD fans). Obviously I don't want to hit uncle google with my tests, so:
Mox.defmock(MyApplication.Accounts.MockRecaptcha, for: MyApplication.Accounts.RecaptchaBehaviour)
inside test_helper.ex
. Needed to define that behaviour separately:
defmodule MyApplication.Accounts.RecaptchaBehaviour do
@callback verify(String.t(), Keyword.t()) :: {:ok, Response.t()} | {:error, [atom]}
@callback verify(String.t()) :: {:ok, Response.t()} | {:error, [atom]}
end
do some tests using:
MyApplication.Accounts.MockRecaptcha
|> expect(:verify, fn _response -> {:ok, _response} end)
So far so good, except... all other tests are now failing with:
** (Mox.UnexpectedCallError) no expectation defined for MyApplication.Accounts.MockRecaptcha.verify/1 in process #PID<0.854.0> with args [nil]
Reading the fine docs I find: "[...] you might want the implementation to fall back to a stub (or actual) implementation when no expectations are defined. stub_with/2
is just what you need!"
So another line in test_helper.ex
:
Mox.stub_with(MyApplication.Accounts.MockRecaptcha, Recaptcha)
That doesn't work because ** (ArgumentError) Recaptcha does not implement any behaviour
, Well.. let's add my own "proxy" then, which does:
defmodule MyApplication.Accounts.Recaptcha do
@behaviour MyApplication.Accounts.RecaptchaBehaviour
def verify(response, options \\ []) do
Recaptcha.verify(response, options)
end
end
And change the test_helper.ex
line to
Mox.stub_with(MyApplication.Accounts.MockRecaptcha, MyApplication.Accounts.Recaptcha)
Now the ArgumentError
is gone but all tests with no Mox expectations fail the same as before. No change with and without the stub_with/2
.
And I feel like I spent already far too much time with it... :-( Any help to put me on track?
As requested in the comments, the failing tests are for example controller tests:
describe "guest GET /signup" do
setup do
System.put_env("RECAPTCHA_SITE_KEY", "123")
{:ok, conn: get(build_conn(), "/signup")}
end
test "returns HTTP_OK", %{conn: conn} do
assert response(conn, 200)
end
test "invokes UserView", %{conn: conn} do
assert Phoenix.Controller.view_module(conn) == MyApplication.UserView
end
test "renders into guest layout", %{conn: conn} do
assert Phoenix.Controller.layout(conn) == {MyApplication.LayoutView, :guest_layout}
end
test "renders 'new' template", %{conn: conn} do
assert Phoenix.Controller.view_template(conn) == "new.html"
end
end
Yes, due to the request being generated they (unnecessarily) "touch" the Recaptcha, which is a different thing and the "solution" is not to make them walk around the Recaptcha but rather to make stub_with/2
do its job.
As discussed on Elixir Forum stub_with/2
cannot be called "globally" like e. g. from test_helper.ex
. It has to be called only either in setup
or directly in the test.