Elixir中测试主管的最佳实践

Elixir中测试主管的最佳实践,elixir,Elixir,我在谷歌上搜索了一段时间,却找不到关于这个主题的任何东西——要么是长生不老药是一种太年轻的语言,要么是我在用错误的术语搜索 我正在学习Jose Valim的Elixir门户教程(),并且正在为实践构建测试(我已经构建了所有的功能) 本教程的一部分是构建一个管理器,使Portal.Door模块具有容错性 我正在尝试使用以下代码测试容错性(例如,如果Portal.Door实例未正确关闭,主管将重新启动该实例) defmodule PortalTest do use ExUnit.Case, as

我在谷歌上搜索了一段时间,却找不到关于这个主题的任何东西——要么是长生不老药是一种太年轻的语言,要么是我在用错误的术语搜索

我正在学习Jose Valim的Elixir门户教程(),并且正在为实践构建测试(我已经构建了所有的功能)

本教程的一部分是构建一个管理器,使Portal.Door模块具有容错性

我正在尝试使用以下代码测试容错性(例如,如果Portal.Door实例未正确关闭,主管将重新启动该实例)

defmodule PortalTest do
  use ExUnit.Case, async: true

  ...

  test "supervisor restarts doors" do 
    {:ok, pid} = Portal.shoot(:third)
    Process.unlink(pid)
    Process.exit(pid, :shutdown)
    assert Portal.Door.get(:third) == [] #new doors initialize with an empty array
  end

end
但我在运行测试时不断遇到此错误:

  1) test supervisor restarts doors (PortalTest)
     test/portal_test.exs:35
     ** (exit) exited in: GenServer.call(:third, {:get, #Function<3.47016826/1 in Portal.Door.get/1>}, 5000)
         ** (EXIT) shutdown
     stacktrace:
       (elixir) lib/gen_server.ex:356: GenServer.call/3
       test/portal_test.exs:39
1)测试主管重新启动车门(PortalTest)
测试/入口测试。exs:35
**(退出)退出:GenServer.call(:third,{:get,#Function},5000)
**(退出)关机
堆栈跟踪:
(elixir)lib/gen_server.ex:356:GenServer.call/3
测试/入口测试。exs:39

所以,我想知道是否有更好的方法来实现这一点,或者我的代码很糟糕。

进程。exit/1
发送退出信号,但不等待进程停止。根据错误输出判断,它看起来像是
Portal.Door.get/1
然后失败,因为gen_服务器进程在接收到调用消息之前终止

要克服这个问题,您需要等待进程关闭,然后再次重新启动。一个简单的补救办法可能是在发出退出信号后,通过
:timer.sleep/1
短暂睡眠(比如100毫秒)

更复杂的方法是等待进程终止,然后再次重新启动。第一部分可以通过设置监视器(通过
Process.monitor/1
)并等待相应的
:DOWN
消息轻松完成。通过这样做,您还可以验证目标进程确实已终止


然后,您需要等待进程再次重新启动,以便发出请求。这可能会很棘手,而短暂的睡眠可能是最简单的选择。或者,如果该进程是在本地别名下注册的,则可以使用
进程进行轮询。whereis/1
直到获得非nil值,此时您知道该进程将再次运行。

这里是一个工作代码示例,主要基于提供的提示@sasajuri

defmodule Namer.Worker.Test do
  use ExUnit.Case

  test "supervisor restarts worker on server crash" do
    pid = Process.whereis(Namer.Worker)
    ref = Process.monitor(pid)
    Process.exit(pid, :kill)
    receive do
      {:DOWN, ^ref, :process, ^pid, :killed} ->
        :timer.sleep 1
        assert is_pid(Process.whereis(Namer.Worker))
    after
      1000 ->
        raise :timeout
    end
  end
end

事实上,我收回了我原来的陈述。似乎当我使用IO.inspect进行一些调试时,它会导致一个足够长的延迟,使进程重新启动。没有它,使用递归等待Process.whereis/1返回除nil值以外的其他值似乎不起作用(可能是我的错)。所以,我现在使用:timer.sleep/1。