【问题标题】:Unit testing a periodic timer via handle_info通过 handle_info 对周期性计时器进行单元测试
【发布时间】:2018-11-27 14:53:54
【问题描述】:

我有一个执行周期性操作的 GenServer,如下所示:

defmodule Hello
  use GenServer
  def init(_) do
    Process.send_after(self(), :timer, 1000)
    {:ok, %{}}
  end

  def handle_info(:timer, state) do
    # do stuff
    Process.send_after(self(), :timer, 1000)
    {:noreply, state}
  end
end

我正在尝试找出对其进行单元测试的最佳方法。我最初的想法是使用mox 来存根 Process.send_after 调用。这很好用,但是在我的单元测试中,我尝试了这样的事情:

test "the timer callback does the right thing" do
  MyMock |> expect(:timer, fn -> :ok end)
  {:ok, pid} = start_supervised(Hello, [])
  Process.send(pid, :timer)
  # assert the right thing happens
end

但是,这不起作用,因为Process.send 是异步的并且不会返回任何内容。我还能如何测试handle_info 回调?

【问题讨论】:

  • handle_info(:timer, _) 有什么具体的东西可以用于断言吗?还是您只想断言该函数已被调用?
  • 对于我的具体场景,定时器回调调用我控制的一个函数(模块传入GenServer的状态。对于我的测试,我是传入一个mox命名空间。但是,我也是对 handle_info 更改状态以响应消息的情况以及如何对其进行测试感兴趣。

标签: unit-testing async-await elixir gen-server


【解决方案1】:

有很多选项,比如可以从 setup_all 回调中生成一个 Agent 并在模拟和断言中使用它:

expect(MyMock, :timer, fn -> Agent.update(MyAgent, & &1 + 1))
...
Process.sleep(1_100)                 # ensure all sent
assert Agent.get(MyAgent, & &1) == 1 # number of calls

可能最简单的方法是捕获 IO:

import ExUnit.CaptureLog
...
test "the timer callback does the right thing" do
  MyMock |> expect(:timer, fn -> IO.inspect(:ok) end)
  {:ok, pid} = start_supervised(Hello, [])

  assert capture_log(fn ->
    Process.send(pid, :timer)
    Process.sleep(1_100)
  end) =~ "ok" 
end

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-23
    • 2020-11-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-18
    相关资源
    最近更新 更多