How to fake I / O when testing with ExUnit?

I have an Elixir program that I would like to test, which introduces the user several times through IO.gets. How could I pretend this is a test entry?

Note. I would like to return a different value for eachIO.gets

+4
source share
2 answers

The preferred way to do this is to split your code into clean (without side effects) and unclean (does io). Therefore, if your code looks like this:

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

try to extract parts between IO.gets into functions that you can test in isolation from 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 , . .

+6

FakeIO . , FakeIO IO

, -, STDIN STDOUT.

?

, , !

"", Ex1:

defmodule Ex1 do

  def sayHello(io \\ IO) do
    "What is your name? "
    |> input(io)
    |> reply
    |> output(io)
  end

  def input(message, io \\ IO) do
    io.gets(message) |> String.trim
  end

  def reply(name) do
   "Hello, #{name}, nice to meet you!"
  end

  def output(message, io \\ IO) do
    io.puts(message)
  end

end

:

defmodule FakeIO do
  defdelegate puts(message), to: IO
  def gets("What is your name? "), do: "Elixir "
  def gets(value), do: raise ArgumentError, message: "invalid argument #{value}"
end

defmodule Ex1Test do
  use ExUnit.Case
  import ExUnit.CaptureIO
  doctest Ex1

  @tag runnable: true
  test "input" do
    assert Ex1.input("What is your name? ", FakeIO) == "Elixir"
  end

  @tag runnable: true
  test "reply" do
    assert Ex1.reply("Elixir") == "Hello, Elixir, nice to meet you!"
  end

  @tag runnable: true
  test "output" do
    assert capture_io(fn ->
      Ex1.output("Hello, Elixir, nice to meet you!", FakeIO)
    end) == "Hello, Elixir, nice to meet you!\n"
  end

  @tag runnable: true
  test "sayHello" do
    assert capture_io(fn ->
      Ex1.sayHello(FakeIO)
    end) == "Hello, Elixir, nice to meet you!\n"
  end

end

FakeIO defdelegate IO.puts. gets "" , ArgumentError, FakeIO -, get param.

 defmodule FakeIO do
   defdelegate puts(message), to: IO
   def gets("What is your name? "), do: "Elixir "
   def gets(value), do: raise ArgumentError, message: "invalid argument #{value}"
 end

, , FakeIO.

+1

All Articles