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

Posted

技术标签:

【中文标题】在 Elixir ExUnit 中,我如何保证 Supervisor 将创建一个新的 GeNserver?【英文标题】:In Elixir ExUnit, how do I guarantee that the Supervisor will create a new GeNserver? 【发布时间】:2019-10-09 07:09:33 【问题描述】:

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

当我运行以下测试时,有时它通过有时不,我认为这是主管没有时间重新启动 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?你不相信 40yo 久经考验的解决方案吗?您应该测试 your 代码,而不是 OTP 代码。后者有效,认为这是理所当然的。 ExUnit 没有提供一种方便的方法来准确地测试它,因为没有人应该测试 OTP 内部。 @AlekseiMatiushkin 我只是想研究 OTP 使用测试的工作方式,例如 TDD 【参考方案1】:

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

    确保旧进程已死。这可以通过在发送终止信号之前监控进程然后assert_receive :DOWN, ^monitor_ref, _, _, _

    来完成

    查询主管,直到活动计数变为 1。这可以通过每 10 毫秒左右执行一次函数来完成

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

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

换句话说,我会与主管测试合同,而不是测试主管本身。

【讨论】:

感谢反馈,我试试这个【参考方案2】:

这是一个时间问题。简短的版本是不要打扰测试 OTP。 IT 已经经过了很好的测试。

但是,如果您将来有需要确保初创公司正常运行的案例,了解 Supervisors 如何启动服务器很有用,请观看此视频 https://www.youtube.com/watch?v=2i_XrY5CCXE

【讨论】:

以上是关于在 Elixir ExUnit 中,我如何保证 Supervisor 将创建一个新的 GeNserver?的主要内容,如果未能解决你的问题,请参考以下文章

Elixir 列表解释为 char 列表

Elixir中的“? s”是什么意思?

如何在 Windows 中拥有最新版本的 Elixir

如何在Elixir中获取变量的内存位置?

如何在 Elixir 主管中引用之前启动的进程

如何重新编译 Elixir 项目并从 iex 中重新加载它?