如何测试 GenServer 重启行为?

How to test GenServer restart behaviour?

在我的应用程序中,我有一个 GenServer。它备份在代理中重新启动所需的数据。我想测试我的 GenServer 是否正确备份和恢复,所以我想启动备份代理,然后重新启动 GenServer 看看它是否有效(记住重新启动之前的配置)。

现在我已经在测试设置中配置并启动了 GenServer(使用 start_supervised!)。我需要以某种方式重新启动该 GenServer。

有什么好的方法吗?我应该完全不同地做吗?是否有不同的、正确的方法来测试重启行为?

主管决定何时通过该子进程的 child_spec 在其监督下重新启动进程。默认情况下,当您定义 GenServer 模块并在模块上使用 use GenServer 时,默认值 (restart values) 将为 :permanent,这意味着,如果它退出,则始终重新启动此进程。

鉴于此,向它发送一个退出信号就足够了,Process.exit(your_gen_server_pid, :kill):kill 将确保即使进程陷入退出也会被杀死),并且主管然后应该重新开始这个过程,然后你就可以做你的断言了。

你需要一种方法来解决 "new" genserver 进程,因为它会被杀死,当重新启动时它的 pid 将与原来的不一样,通常你这样做通过在启动时提供名称。

如果您的 genserver 加载状态作为其 init 的一部分,您不一定需要监督它来测试备份行为,您可以单独启动它,终止它,然后再次启动它.

可能会出现一些边缘情况,具体取决于您如何建立备份等,但通常这就足够了。

更新:

要解决进程退出和重新启动的问题,您可以编写 2 个辅助函数来专门处理这个问题。

def ensure_exited(pid, timeout \ 1_000) do
  true = Process.alive?(pid)
  ref = Process.monitor(pid)
  Process.exit(pid, :kill)
  receive do
     {:DOWN, ^ref, :process, ^pid, _reason} -> :ok
  after
    timeout -> :timeout
  end
end

您可以改为使用 name 并执行 GenServer.whereis 来检索 pid,但想法是一样的。

为了确保它还活着:

def is_back_up?(name, max \ 200, tries \ 0) when tries <= max do
    case GenServer.whereis(name) do
       nil ->
         Process.sleep(5)
         is_back_up?(name, max, tries + 1)
       pid -> true
    end
end

def is_back_up?(_, _, _), do: false

基本思路就是这样。不确定是否已经有一些助手可以做这种事情。

然后你就可以使用它(你可以编写一个第三个助手来获取实时 pid,名称,并在一个 "step" 中完成),或者写:

:ok = ensure_exited(pid)
true = is_back_up?(name)