在 Elixir ExUnit 中,我如何保证 Supervisor 会创建一个新的 GeNserver?

In Elixir ExUnit, how do I guarantee that the Supervisor will create a new GeNserver?

我正在学习在 Elixir 中进行测试,出现了这个问题:

当我运行以下测试时,有时它通过有时不通过,我认为这是Supervisor没有时间重新启动GenServer的事实:

  test "Supervisor will revive the dead gensever" do
    {:ok, pid} = KV.Supervisor.start_link([])
    KV.RegistryClient.create KV.RegistryClient, "wallet"
    [h | _] = Supervisor.which_children(pid)
    {KV.RegistryClient, pid_reg, _, _} = h
    send(pid_reg, :insta_kill)

    assert %{active: 1} = Supervisor.count_children(pid)
  end

发生时,这是错误:

1) test Supervisor will revive the dead gensever (KV.RegistryTest)
     test/kv/registry_test.exs:35
     match (=) failed
     code:  assert %{active: 1} = Supervisor.count_children(pid)
     right: %{active: 0, specs: 1, supervisors: 0, workers: 1}
     stacktrace:
       test/kv/registry_test.exs:41: (test)

如何防止这种情况发生?超时是一个好方法吗?

这是时间问题。简短的版本是不要费心测试 OTP。 IT 已经经过很好的测试。

但如果您将来遇到需要确保启动正常工作的情况,了解 Supervisors 如何启动服务器很有用,请观看此视频 https://www.youtube.com/watch?v=2i_XrY5CCXE

在没有竞争条件的情况下有效测试此行为的唯一方法是:

  1. 确保旧进程已死。这可以通过在发送 kill 信号之前监视进程然后 assert_receive {:DOWN, ^monitor_ref, _, _, _}

  2. 来完成
  3. 查询supervisor,直到活跃计数变为1。这可以通过每 10 毫秒左右执行一次函数来完成

但是,正如其他人所说,Erlang/OTP 保证了这种行为。因此,我宁愿测试您是否将正确的子规范传递给主管,而不是测试主管是否真的重新启动了某些东西。所以假设主管开始一个基于 KV.Registered 的处理,我会这样做:

assert %{restart: :permanent} = Supervisor.child_spec(KV.Registered)

换句话说,我会与监管者一起测试合同,而不是监管者自己测试。