使用 Mock 或 Mox 模拟 Elixir 中的连续函数调用

Mocking consecutive function calls in Elixir with Mock or Mox

我正在尝试模拟函数多次调用,所以它 returns 每次都有特定的不同值。我不太熟悉 Elixir 和功能概念。

defmodule Roller do
  def roll do
      1..10
      |>Enum.random()
  end
end

滚轮returns每次调用一个随机数。

defmodule VolunteerFinder do
  import Roller

  def find(list) do
    find(list, []);
  end

  defp find([] = _list, result) do
    result
  end

  defp find([head | tail] = _list, result) do
    result = [%{name: head, score: Roller.roll()} | result]
    find(tail, result)
  end
end

所以假设list包含不止一个元素,滚子被调用了2次。在我的测试中,我需要以某种方式控制它。

我用 Mock 试过了。我想以最简单的方式做这样的事情。如果不必在任何地方保存一些状态或 运行 为每个调用单独处理,那就太好了。我知道 Elixir 的想法可能与我的 objective 范式思维模式略有不同。 Elixir 测试 VolunteerFinder 模块的最正确 方法是什么?

defmodule VolunteerFinderTest do
  use ExUnit.Case

  import Mock
  import Roller

  test_with_mock(
    "Find volunteer for list with one element",
    Roller,
    [roll: fn() -> 5 end]
  ) do
    assert VolunteerFinder.find(["John"]) == [%{name: "John", score: 5}]
  end


  test_with_mock(
    "Find volunteer for list with two elements",
    Roller,
    [roll: fn
       () -> 2
       () -> 5
    end]
  ) do
    assert VolunteerFinder.find(["John", "Andrew"])
    == [%{name: "Andrew", score: 5}, %{name: "John", score: 2}]
  end
end

我找到了一个可行的解决方案,但我不确定自己是否满意:

  test_with_mock(
    "Find volunteer for list with two elements",
    Roller,
    [roll: fn () -> mock_roll end]
  ) do
    send self(), {:mock_return, 1}
    send self(), {:mock_return, 5}

    assert VolunteerFinder.find(["Sergey", "Borys"])
    == [%{name: "Borys", score: 5}, %{name: "Sergey", score: 1}]
  end

  def mock_roll do
    receive do
      {:mock_return, value} ->
        value
    after
      0 ->
        raise "No mocked returns received"
    end
  end

有没有更优雅的解法?