I am working on a Phoenix application that also uses a GenServer to fetch some data from a third party API on startup and periodically to refresh the values it got.
I’ve got everything working but then I wanted to add some tests, using a mock in place of the external service and I’m having some problems. I’m using mox
following what is described in the documentation and in the blog post.
My GenServer looks like this:
defmodule MyApp.Collector do
use GenServer
@api Application.get_env(:my_app, :third_party_api)
def start_link() do
GenServer.start_link(__MODULE__, %{}, name: __MODULE__)
end
defp fetch_new_state() do
@third_party_api.lookup()
# put the data into an Agent
end
defp schedule_work() do
refresh_interval = 10 * 60 * 1000
Process.send_after(self(), :refresh, refresh_interval)
end
def init(state) do
schedule_work()
fetch_new_state()
{:ok, state}
end
def handle_info(:refresh, state) do
schedule_work()
fetch_new_state()
{:noreply, state}
end
def handle_info(request, state) do
super(request, state)
end
end
The problem I have is that the call to fetch_new_state
in the init
callback would call the mock before I have a chance to set any expectation: (Mox.UnexpectedCallError) no expectation defined for MyApp.ThirdPartyApi.Mock.lookup/0 in process #PID<0.293.0>
I tried to add expectations as early as possible in test_helper.ex
, but that didn’t help because mix
tries to start all the processes in the supervision tree as part of the mix
task, which I believe always happen before any call to test_helper.ex
.
I could use mix test --no-start
to prevent mix
from starting the supervision tree, but that feels wrong because then I would need to manually start all the applications in the correct order.
Another possible workaround is to just schedule some work to be done in init
, without calling the api but I’m not very convinced by this approach either.
Is it fine to do some work in the init
callback of a GenServer? If it is, how do you handle a scenario like the one I described above?