testing - 使用 ExUnit 进行测试时如何伪造 IO 输入?

标签 testing functional-programming elixir ex-unit

我有一个 Elixir 程序,我想测试它通过 IO.gets 多次从用户那里获取输入。我将如何在测试中伪造这个输入?

注意:我想为每个 IO.gets

返回不同的值

最佳答案

首选的方法是将您的代码分成纯代码(没有副作用)和不纯代码(有 io)。因此,如果您的代码如下所示:

IO.gets
...
...
...
IO.gets
...
...

尝试将 IO.gets 之间的部分提取到可以独立于 IO.gets 进行测试的函数中:

def fun_to_test do
  input1 = IO.gets
  fun1(input1)
  input2 = IO.gets
  fun2(input2)
end

然后您可以单独测试这些功能。这并不总是最好的做法,尤其是当不纯部分深藏在 ifcasecond 语句中时。

另一种方法是将 IO 作为显式依赖项传递:

def fun_to_test(io \\ IO) do
  io.gets
  ...
  ...
  ...
  io.gets
  ...
  ...
end

这样您就可以在生产代码中使用它而无需任何修改,但在您的测试中您可以将它传递给一些不同的模块 fun_to_test(FakeIO)。如果提示不同,您可以在 gets 参数上进行模式匹配。

defmodule FakeIO do
  def gets("prompt1"), do: "value1"
  def gets("prompt2"), do: "value2"
end

如果它们始终相同,您需要保持 gets 被调用次数的状态:

defmodule FakeIO do
  def start_link do
    Agent.start_link(fn -> 1 end, name: __MODULE__)
  end

  def gets(_prompt) do
    times_called = Agent.get_and_update(__MODULE__, fn state ->
      {state, state + 1}
    end)
    case times_called do
      1 -> "value1"
      2 -> "value2"
    end
  end
end

最后一个实现是一个具有内部状态的完整工作模拟。在测试中使用它之前,您需要调用 FakeIO.start_link。如果这是你需要在很多地方做的事情,你可以考虑一些模拟库,但正如你所看到的——这并不太复杂。为了使 FakeIO 更好,您可以打印提示。我在这里跳过了这个细节。

关于testing - 使用 ExUnit 进行测试时如何伪造 IO 输入?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37715885/

相关文章:

c# - 集成测试入门

haskell - 功能阵列倍增堆栈的摊销

ios - swift中的嵌套递归函数

c++ - 构造函数初始化列表中可变参数的 Lambda 捕获

linux - 我无法在 Linux 中创建项目

internationalization - 在 Phoenix 框架中使用 hex linguist 包

c# - 如何发现哪个测试单元检查了哪些代码行?

testing - 需求和测试用例之间的关系是什么?

testing - Selenium:使用 selenium 网格存储屏幕截图

javascript - 如何在 Elixir 中编写多事件驱动的 if 子句?