每次测试后停止 GenServer
Stop a GenServer after each test
背景
我有一组测试需要先启动 GenServer。根据经验,我知道在每次测试后进行清理是一个很好的做法,所以我也想在每次测试后停止 GenServer。
问题
这里的问题是我不知道如何在测试完成后停止 GenServer。我总是遇到一些并发问题。
defmodule MyModuleTest do
use ExUnit.Case
alias MyModule
setup do
MyModule.Server.start_link(nil)
context_info = 1
more_info = 2
%{context_info: context_info, more_info: more_info}
end
describe "some tests" do
test "returns {:ok, order_id} if order was deleted correctly", context do
# do test here that uses created server and passed context
assert actual == expected
#clean up?
end
end
end
现在,我已尝试 on_exit
/2 如下所示:
setup do
{:ok, server} = MyModule.Server.start_link(nil)
context_info = 1
more_info = 2
on_exit(fn -> GenServer.stop(server) end)
%{context_info: context_info, more_info: more_info}
end
但是我得到这个错误:
** (exit) exited in: GenServer.stop(#PID<0.296.0>, :normal, :infinity)
** (EXIT) no process: the process is not alive or there's no process currently associated with the given name, possibly because its application isn't started
我觉得这退出得太早了。
我也尝试过使用 start_supervised
,但是因为我的 GenServer
在 handle_continue
测试 运行 中进行了冗长的初始化,然后服务器就绪。
问题
我该如何解决这个问题?
我终于明白是怎么回事了
原来 start_supervised
正在按预期工作,实际上 正在等待 GenServer 结束 handle_continue
(好吧,它不是 确实等待,它仍然发送消息,这些消息被放入队列等待适当的时间执行)。
这里的问题是我没有完全清理我在 handle_continue
中启动的所有内容。事实证明,即使在原始 GenServer 死后,我开始的一些连接和进程仍然存在。
解决方案是双重的:
- 捕捉所有停止信号
- 检测到停止信号后,进行正常关机
在代码中,这被翻译成:
def init(_) do
Process.flag(:trap_exit, true) # trap all exits!
{:ok, %{}, {:continue, :setup_queue}}
end
def handle_continue(:setup_queue, state) do
# do heavy lifting
{:noreply, state}
end
def terminate(_reason, state), do:
# undo heavy lifting
有了这个和 start_supervised
在我的 setup
块中,一切都很好。
背景
我有一组测试需要先启动 GenServer。根据经验,我知道在每次测试后进行清理是一个很好的做法,所以我也想在每次测试后停止 GenServer。
问题
这里的问题是我不知道如何在测试完成后停止 GenServer。我总是遇到一些并发问题。
defmodule MyModuleTest do
use ExUnit.Case
alias MyModule
setup do
MyModule.Server.start_link(nil)
context_info = 1
more_info = 2
%{context_info: context_info, more_info: more_info}
end
describe "some tests" do
test "returns {:ok, order_id} if order was deleted correctly", context do
# do test here that uses created server and passed context
assert actual == expected
#clean up?
end
end
end
现在,我已尝试 on_exit
/2 如下所示:
setup do
{:ok, server} = MyModule.Server.start_link(nil)
context_info = 1
more_info = 2
on_exit(fn -> GenServer.stop(server) end)
%{context_info: context_info, more_info: more_info}
end
但是我得到这个错误:
** (exit) exited in: GenServer.stop(#PID<0.296.0>, :normal, :infinity)
** (EXIT) no process: the process is not alive or there's no process currently associated with the given name, possibly because its application isn't started
我觉得这退出得太早了。
我也尝试过使用 start_supervised
,但是因为我的 GenServer
在 handle_continue
测试 运行 中进行了冗长的初始化,然后服务器就绪。
问题
我该如何解决这个问题?
我终于明白是怎么回事了
原来 start_supervised
正在按预期工作,实际上 正在等待 GenServer 结束 handle_continue
(好吧,它不是 确实等待,它仍然发送消息,这些消息被放入队列等待适当的时间执行)。
这里的问题是我没有完全清理我在 handle_continue
中启动的所有内容。事实证明,即使在原始 GenServer 死后,我开始的一些连接和进程仍然存在。
解决方案是双重的:
- 捕捉所有停止信号
- 检测到停止信号后,进行正常关机
在代码中,这被翻译成:
def init(_) do
Process.flag(:trap_exit, true) # trap all exits!
{:ok, %{}, {:continue, :setup_queue}}
end
def handle_continue(:setup_queue, state) do
# do heavy lifting
{:noreply, state}
end
def terminate(_reason, state), do:
# undo heavy lifting
有了这个和 start_supervised
在我的 setup
块中,一切都很好。