我正在尝试对我正在编写的简单终端应用程序进行集成测试。 这个应用程序基本上是一个主管,它监督其他较小的库。
项目内的通信大多是异步的,进程
send
向调用者发送消息。
bypass
来实现此目的。然而,这个问题却令我感到惊讶。这是测试:
setup do
bypass = Bypass.open(port: 8082)
credentials = %{email: "an_email", pass: "a_password"}
%{
credentials: credentials,
bypass: bypass
}
end
test "logs in user correctly", %{bypass: bypass, credentials: credentials} do
Bypass.expect_once(bypass, fn conn ->
IO.inspect(conn.request_path, label: "REQ PATH")
IO.inspect(conn.method, label: "METHOD")
Plug.Conn.resp(conn, 429, ~s<{"errors": [{"code": 88, "message": "Rate limit exceeded"}]}>)
end)
# start the application
_manager_pid = start_link_supervised!(ManagerSupervisor)
# this should make a GET request to bypass but ...
Manager.login(credentials, false)
end
如果我只是运行
mix test
这个简单的测试就会失败,因为 Bypass 没有收到任何请求。
1) test login logs in user correctly (Manager.WorkerTest)
test/integration/manager_test.exs:83
No HTTP request arrived at Bypass
但是,令人困惑的是,如果我通过运行
--breakpoints
使用 iex -S mix test --breakpoints
功能,我可以通过执行一些简单的步骤来使测试通过:
n
。Manager.login(credentials, false
)时,我不使用n
或c
执行它。相反,我手动输入命令Manager.login(credentials, false)
。bypass
电话c
,测试通过了这让我很困惑。看来测试过程没有调用
Manager.login
,就像它应该的那样。
我无法解释这一点。
bypass
选项手动调用 Manager.login
时会调用 --breakpoint
,而不是在 mix test
运行时调用?经过更多调查,我意识到运行测试的测试过程完成得太快。事实证明,绕过 http 请求以及代码必须执行的其他 IO 任务的延迟时间太长,因此测试将在运行测试的进程收到任何消息之前结束。
为此,即使@Peaceful James使用
Process.sleep
的建议可行,我还是决定采用不同的方法,使用assert_receive
,它确实有一个超时选项,它强制测试进程等待设定的时间,然后如果没有收到接收给定模式的消息,则显示进程的整个邮箱。
最终测试看起来像这样:
test "logs in user correctly", %{bypass: bypass, credentials: credentials} do
Bypass.expect_once(bypass, fn conn ->
IO.inspect(conn.request_path, label: "REQ PATH")
IO.inspect(conn.method, label: "METHOD")
Plug.Conn.resp(conn, 429, ~s<{"errors": [{"code": 88, "message": "Rate limit exceeded"}]}>)
end)
# start the application
_manager_pid = start_link_supervised!(ManagerSupervisor)
# this should make a GET request to bypass but ...
Manager.login(credentials, false)
assert_receive({:login, :ok}, 5000) # 5 secs timeout
end
原始解决方案的来源可以在这篇文章中找到: