如何integration_test那种genserver?正确使用assert_receive?
How to integration_test that kind of genserver? Proper use of assert_receive?
我有一个应用程序远程连接到另一个节点。应用程序需要能够使用此节点调用远程函数。它在从 iex 调用时有效,但我真的很难让我的集成测试正确。我想检查远程应用程序的 return 是什么,它是否符合预期。
这是我的 genserver 的代码(也欢迎代码见解,但仍然不太适应):
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
测试:
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
所以 assert_receive {_, {:ok, _}}, 3000
行不通,很明显……我尝试了很多方法来塑造它,但没有找到它应该如何工作。我想做的是检查调用的 handle_info 是否正确,数据是否符合预期。
主要是关于 assert_receive
行为。
我遇到了与此类似的问题,但在测试中我没有使用 assert_receive
,而是通过使用 Erlangs 的 :sys.get_state/1
解决了这个问题,您必须传递的参数是pid()
。此函数将等到进程邮箱中的所有消息都已处理,然后它将 return 该进程的状态。因此,在获得状态后,您可以与您期望更改的值进行比较。
解决方案是使用类似
的方式来跟踪传入消息
:erlang.trace(pid, true, [:receive])
然后你用
监视消息
assert_received {:trace, ^pid, :receive, {:"$gen_call", _, :something}}
确保call
有效,然后
:timer.sleep(100) # Just to make sure not tu run into a race condition
assert_receive {:trace, ^pid, :receive, {ref, :returned_data}}
我有一个应用程序远程连接到另一个节点。应用程序需要能够使用此节点调用远程函数。它在从 iex 调用时有效,但我真的很难让我的集成测试正确。我想检查远程应用程序的 return 是什么,它是否符合预期。
这是我的 genserver 的代码(也欢迎代码见解,但仍然不太适应):
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
测试:
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
所以 assert_receive {_, {:ok, _}}, 3000
行不通,很明显……我尝试了很多方法来塑造它,但没有找到它应该如何工作。我想做的是检查调用的 handle_info 是否正确,数据是否符合预期。
主要是关于 assert_receive
行为。
我遇到了与此类似的问题,但在测试中我没有使用 assert_receive
,而是通过使用 Erlangs 的 :sys.get_state/1
解决了这个问题,您必须传递的参数是pid()
。此函数将等到进程邮箱中的所有消息都已处理,然后它将 return 该进程的状态。因此,在获得状态后,您可以与您期望更改的值进行比较。
解决方案是使用类似
的方式来跟踪传入消息:erlang.trace(pid, true, [:receive])
然后你用
监视消息assert_received {:trace, ^pid, :receive, {:"$gen_call", _, :something}}
确保call
有效,然后
:timer.sleep(100) # Just to make sure not tu run into a race condition
assert_receive {:trace, ^pid, :receive, {ref, :returned_data}}