Reputation: 4539
I have an app remotely connected to another one’s node. The app needs to be able to call a distant function using this node. It works when called from iex, but I really struggle to get my integration tests right. I would like to check what is the return of the remote app, and if it fits what is expected.
Here is my genserver’s code (code insights welcome as well, still not really comfortable with it) :
defmodule MyApp.MyExternalAppModule do
use GenServer
@external_app_node Application.get_env(:my_app, :external_app_node)
@mailer Application.get_env(:my_app, :mailer)
def start_link(_args) do
GenServer.start_link(__MODULE__, %{}, name: __MODULE__)
end
def insert(field1, field2, field3) do
GenServer.call(__MODULE__, {:insert, field1, field2, field3})
end
def init(%{}) do
{:ok, %{ref: nil}}
end
def handle_call(
{:insert, _field1, _field2, _field3},
_from,
%{ref: ref} = state
)
when is_reference(ref) do
{:reply, :ok, state}
end
def handle_call({:insert, field1, field2, field3}, _from, %{ref: nil}) do
task =
Task.Supervisor.async_nolink(
{MyExternalApp.TaskSupervisor, @external_app_node},
MyExternalApp.MyExternalAppModule,
:my_function,
[field1, field2, field3]
)
{:reply, :ok, %{field1: field1, field2: field2, field3: field3, ref: task.ref}}
end
def handle_info(
{ref, {:ok, _external_element}},
%{ref: ref, field1: field1, field2: field2, field3: field3} = state
) do
Process.demonitor(ref, [:flush])
@mailer.send_mail("(...)success")
{:noreply, %{state | ref: nil}}
end
def handle_info(
{ref, {:error, reason}},
%{ref: ref, field1: field1, field2: field2, field3: field3} = state
)
when is_atom(reason) do
Process.demonitor(ref, [:flush])
@mailer.send_mail("(...)failure")
{:noreply, %{state | ref: nil}}
end
def handle_info(
{ref, {:error, _changeset}},
%{ref: ref, field1: field1, field2: field2, field3: field3} = state
) do
Process.demonitor(ref, [:flush])
@mailer.send_mail("(...)failure")
{:noreply, %{state | ref: nil}}
end
end
Tests :
defmodule MyApp.MyExternalAppModuleTest do
use ExUnit.Case, async: true
@my_external_app_module Application.get_env(:my_app, :my_external_app_module)
describe "insert/3" do
test "when my_external_app node is up and the data doesn't exist returns (TODO)" do
assert_receive {_, {:ok, _}}, 3000
assert :ok == @my_external_app_module.insert("field1", "field2", "field3")
end
end
end
So assert_receive {_, {:ok, _}}, 3000
doesn’t work, obviously… I tried to mold it in a lot of ways without finding how it should work. What I want to do is check that it’s the right handle_info that is called and the data is as expected.
Mostly about assert_receive
behavior that is.
Upvotes: 1
Views: 216
Reputation: 4539
The solution would be to trace incoming messages with something like
:erlang.trace(pid, true, [:receive])
And then you watch for messages with
assert_received {:trace, ^pid, :receive, {:"$gen_call", _, :something}}
Making sure the call
is effective, and then
:timer.sleep(100) # Just to make sure not tu run into a race condition
assert_receive {:trace, ^pid, :receive, {ref, :returned_data}}
Upvotes: 0
Reputation: 1377
I had similar problem to this one, but in the tests I did not use assert_receive
, instead I have solved it through using Erlangs' :sys.get_state/1
, where the argument you must pass is a pid()
. This functions will wait until all messages in the mail box of the process are processed and then it will return the state of that process. So after getting the state you can compare with the values you were expecting to be changed.
Upvotes: 1