如何在没有模拟的单元测试中记录/验证方法调用?

时间:2015-09-22 18:46:25

标签: unit-testing immutability elixir

我如何在单元测试中写下期望?

以下是一个例子:

defmodule MyAPIModule do
  @url "http://example.com/path"

  def get_something(http_module \\ HTTPoison) do
    http_module.get!(@url, [], [])
  end
end

这是我的测试:

defmodule MyAPIModuleTest do
  use ExUnit.Case

  test "Runs the get! method with no headers and no params" do
    MyAPIModule.get_something(HttpSpy)
    # 1) assert that get! was called
    # 2) assert [] == headers
    # 3) assert [] == params
    # 4) assert "http://example.com/path" == url
  end
end

defmodule HttpSpy do
  def get!(url, headers, params) do
    #
  end
end

我的问题是不变性。我不能简单地在HttpSpy模块中创建一个列表,并将方法调用及其参数记录到该列表中。

所以我尝试用这种方式改变HttpSpy:

defmodule HttpSpy do
  def start(listener) do
    spawn_link(__MODULE__, loop, [listener])
  end

  def loop(listener) do
    receive do

    end
  end

  def get!(url, headers, params) do

  end
end

我将测试更改为:

test "Runs the get! method with no headers and no params"
  spy = HttpSpy.start(self)
  MyAPIModule.get_something(HttpSpy)
  receive do
    {^spy, method_call} -> flunk("wrong method call")
    after 1000 -> flunk("timeout")
  end
end
但是我被困住了。 HttpSpy.get!不知道测试产生的HttpSpy过程的PID。 因此HttpSpy.get!无法向循环方法中等待的进程发送消息。 因此,循环方法永远不会向测试(我称之为监听器)发送被调用的函数及其参数。

我意识到我想要的基本上是模拟和期望,但我想找到惯用的方式(=没有嘲笑)来做它。

2 个答案:

答案 0 :(得分:3)

帕特里克给出了一个很好的答案,这将是我的第一个方法。但是,如果您无法直接访问间谍返回的结果,您只需向自己发送一条消息:

# In the spy
send self(), :get_was_called

# In the test
assert_received :get_was_called 

答案 1 :(得分:2)

您可以从假get!函数返回所需的值,提取它们然后断言结果。您将隐式知道调用了正确的函数,因为它返回了预期的结果。

defmodule FakeHttp do
  def get!(url, headers, params) do
    {:get!, url, headers, params}
  end
end

defmodule MyAPIModuleTest do
  use ExUnit.Case

  test "Runs the get! method with no headers and no params" do
    {:get!, url, headers, params} =
      MyAPIModule.get_something(FakeHttp)

    assert [] == headers
    assert [] == params
    assert "http://example.com/path" == url
  end
end

无论如何,我认为断言调用特定函数并不是最重要的事情。我认为对外部可见结果进行断言更有用。这样,只要新实现产生相同的结果,您就可以换出底层实现,并且您的测试仍会通过。