我有一个 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
然后您可以单独测试这些功能。这并不总是最好的做法,尤其是当不纯部分深藏在 if
、case
或 cond
语句中时。
另一种方法是将 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/