silverdr

silverdr

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… :frowning: Any help to put me on track?

Showing Posts 1 to 10

silverdr

silverdr OP

Since nobody seems to be able to step-in I added a

TL;DR

Unrelated tests fail because “no expectation defined” when using Mox library and stub_with/2 doesn’t seem to be of any help

and posted this question on SO:

travisf

travisf

I am by no means an expert on mocks, but recently I have implemented a few. I found this article super helpful. Specifically he talks about using environments to replace the module, in your case, ReCapthca, in the tests.

So in the modules where the recaptcha is called you would replace Recaptcha.verify(response, options) with:

recaptcha().verify(response, options)
...
#getting the actual Recaptch module from the environment
defp recaptcha do 
   Application.get_env(:my_app, :reacaptcha)
end 

Then in your test_helper.exs under the mock definition you can add:
Application.put_env(:my_app, :recaptcha, MyApplication.Accounts.MockRecaptcha).

Remember to add: config :my_app, :recaptcha, MyApplication.Accounts.Recaptcha to your config.exs so the Recaptcha works in all other environments.

That should enable the Mox expect/2 to work in the test.

I’m not sure about the Mox.stub_with/2 function in the test_helper, I haven’t played with Mox stubs.

ruslandoga

ruslandoga

I have a telegram bot client in my app which uses both mocks and stubs during tests and it seems to work fine. Here’s it’s setup:

config/test.exs

config :app, App.Bot, adapter: MockBot

lib/bot.ex

defmodule App.Bot do
  # I'm using compile-time configuration
  # https://hexdocs.pm/mox/Mox.html#module-compile-time-requirements
  # it's probably not important
  @adapter Application.compile_env!(:app, [__MODULE__, :adapter])
  
  def set_webhook(url) do
    @adapter.set_webhook(url)
  end
  
  # ...
end

lib/bot/adapter.ex

defmodule App.Bot.Adapter do
  @moduledoc false
  @callback set_webhook(url :: String.t()) :: :ok | {:error, map}
  # ...
end

test/support/mocks.exs

Mox.defmock(MockBot, for: App.Bot.Adapter)

test/support/stub_bot.ex

defmodule StubBot do
  @behaviour App.Bot.Adapter

  @impl true
  def set_webhook(_url), do: :ok

  # ...
end

and finally test/support/data_case.ex and other helper cases for tests that end up touching the bot module have Mox.stub_with/2 in setup:

defmodule App.DataCase do
  use ExUnit.CaseTemplate

  # ...

  setup tags do
    # ...

    Mox.stub_with(MockBot, StubBot)

    # ...
  end
end
silverdr

silverdr OP

Thank you for sharing. Do you have anything else (like tests in other files) hitting the code of your @App.Bot.Adapter? Or – IOW – if you remove the stub_with do you get the same error(s) as I do (about “no expectation defined”) from other test files?

ruslandoga

ruslandoga

Do you have anything else (like tests in other files) hitting the code of your @App.Bot.Adapter ?

Yes, the bot logs “important” messages, so the stub is executed somewhat often in tests.

silverdr

silverdr OP

expect/2 works as.. well.. expected. The problem is with unrelated tests that now fail because of “no expectation defined”, for which the stub_with/2 is supposed to be a cure

silverdr

silverdr OP

And – just to make sure – you didn’t have to adapt/modify any of the other test files, did you?

ruslandoga

ruslandoga

Are asking if I had to add Mox.expect everywhere?

silverdr

silverdr OP

Or anything for that matter, that would make them work normally.

Yet I was writing this at the same time while you were checking whether removing the stub_with/2 from test_halper.ex makes any difference. And if I add the stub_with/2 to e.g. conn_case.ex then test files, which use MyApplication.ConnCase pass (!). And I guess that if you left the line in test_helper.ex and removed it from data_case.ex you’d encounter the same issue I have. If that’s the case then - WTH? Test files “see” the defmock but don’t “see” the stub_with defined in the very same (test_helper.ex) file?

silverdr

silverdr OP

Right - if I put the stub_with into both conn_case and data_case then all tests work normally. IOW – @ruslandoga – you’re my saviour, man! I guess it has to be invoked always in the given test file context. If I now re-read the doc, the wording might actually imply this… gosh!

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
nseaSeb
Hello, I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
FlyingNoodle
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New
ryanwinchester
apply_graft/2 doesn’t rewrite an add_many sub-workflow’s deps on an add step. Grafted jobs cancel with “upstream job was deleted” Version...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge &amp; Solve. They are GUI (Emerge) and State management (S...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews