Enfoque 1: utilizando meck
Este código, prueba, debe hacer exactamente lo que está pidiendo. Hace algunos trucos de Meck bastante avanzados (especialmente cuando llama al meck:passthrough/0
), pero creo que todavía está muy claro.
% UUT
foo() ->
io:format("Look ma no newlines"),
io:format("more ~w~n", [difficult]),
io:format("~p dudes enter a bar~n", [3]),
ok.
% Helper: return true if mock Mod:Fun returned Result at least once.
meck_returned(Mod, Fun, Result) ->
meck_returned2(Mod, Fun, Result, meck:history(Mod)).
meck_returned2(_Mod, _Fun, _Result, _History = []) ->
false;
meck_returned2(Mod, Fun, Result, _History = [H|T]) ->
case H of
{_CallerPid, {Mod, Fun, _Args}, MaybeResult} ->
case lists:flatten(MaybeResult) of
Result -> true;
_ -> meck_returned2(Mod, Fun, Result, T)
end;
_ -> meck_returned2(Mod, Fun, Result, T)
end.
simple_test() ->
% Two concepts to understand:
% 1. we cannot mock io, we have to mock io_lib
% 2. in the expect, we use passthrough/0 to actually get the output
% we will be looking for in the history! :-)
ok = meck:new(io_lib, [unstick, passthrough]),
meck:expect(io_lib, format, 2, meck:passthrough()),
?assertMatch(ok, foo()),
%?debugFmt("history: ~p", [meck:history(io_lib)]),
?assert(meck_returned(io_lib, format, "Look ma no newlines")),
?assert(meck_returned(io_lib, format, "more difficult\n")),
?assert(meck_returned(io_lib, format, "3 dudes enter a bar\n")),
?assertNot(meck_returned(io_lib, format, "I didn't say this!")),
?assert(meck:validate(io_lib)).
Enfoque 2: utilizando mock_io
Más recientemente (mayo de 2017) escribí mock_io, de una manera muy sencilla para burlarse tanto de entrada como de salida de la unidad bajo prueba, mediante la implementación del Erlang I/O protocolo.
Con mock_io, el código equivalente se convierte en:
% UUT
foo() ->
io:format("Look ma no newlines"),
io:format("more ~w~n", [difficult]),
io:format("~p dudes enter a bar~n", [3]),
ok.
simple_test() ->
Expected = <<"Look ma no newlines"
"more difficult\n",
"3 dudes enter a bar\n">>,
{Pid, GL} = mock_io:setup(),
?assertMatch(ok, foo()),
?assertEqual(Expected, mock_io:extract(Pid)),
mock_io:teardown({Pid, GL}).
Nótese también que mock_io permite inyectar datos en el canal de entrada del TUS, ya sea la entrada estándar o cualquier otro canal. Por ejemplo:
% UUT
read_from_stdin() ->
io:get_line("prompt").
% Test
inject_to_stdin_test() ->
{IO, GL} = mock_io:setup(),
mock_io:inject(IO, <<"pizza pazza puzza\n">>),
?assertEqual("pizza pazza puzza\n", uut:read_from_stdin()),
?assertEqual(<<>>, mock_io:remaining_input(IO)),
mock_io:teardown({IO, GL}).
Una respuesta muy detallada, necesitaré un poco de tiempo para entender su sugerencia. Gracias. – Max