首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >EUnit和io:格式

EUnit和io:格式
EN

Stack Overflow用户
提问于 2010-12-02 11:25:36
回答 5查看 3.2K关注 0票数 7

我想用EUnit测试一些第三方Erlang代码。

代码函数的输出将使用io:format/2显示到标准输出。我想捕获输出,并对打印出来的字符串执行?assert测试。我不能修改第三方代码。

是用Erlang做这件事的方法吗?(例如,在Java中,我可以简单地对输出流使用System.setOut() )。

更新:

group_leader/2似乎走在正确的轨道上。

但是,我仍然不明白这是如何允许我捕获由io:format打印的字符串,以便测试我的断言。代码的一个非常简单的示例是:

代码语言:javascript
复制
result(Value) ->
    io:format("Result: ~w~n", [Value]).

test_result() ->
    ?assertMatch("Result: 5~n", result(5)).

显然,函数result/1的返回是原子ok,但实际上我想测试输出到控制台的字符串(即"Result: 5~n")。

我是否错了这种方法,因为似乎没有其他人这样做(从我缺乏搜索结果判断)?

背景:第三方代码是一个交互式控制台应用程序,所以所有函数都使用io:format来显示结果。

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2010-12-03 17:37:04

为此您可以使用dbg ( Erlang跟踪器)。您可以通过进程跟踪对io:format/2的调用,并从其中接收跟踪消息。您可以使用此跟踪消息断言用于调用io:format/2,3的内容是正确的。这样做的好处是您不必干预EUnit,因为它已经捕获了实际的IO消息。

一个小例子可以是(调整单元测试):

代码语言:javascript
复制
1> HandleFun = fun(Trace, Parent) -> Parent ! Trace, Parent end.
#Fun<erl_eval.12.113037538>
2> dbg:tracer(process, {HandleFun, self()}).
{ok,<0.119.0>}
3> IOCallingFun = fun(F) -> 
3>   timer:sleep(5000),
3>   io:format("Random: ~p~n",[random:uniform(1000)]), 
3>   F(F) 
3> end.
#Fun<erl_eval.6.13229925>
4> PidToTrace = erlang:spawn_link(fun() -> IOCallingFun(IOCallingFun) end).
<0.123.0>
Random: 93
Random: 444
5> dbg:p(PidToTrace, [c]).
{ok,[{matched,nonode@nohost,1}]}
6> dbg:tp(io, format, []).
{ok,[{matched,nonode@nohost,3}]}
Random: 724
Random: 946 
Random: 502 
7> flush().
Shell got {trace,<0.123.0>,call,{io,format,["Random: ~p~n",[724]]}}
Shell got {trace,<0.123.0>,call,{io,format,["Random: ~p~n",[946]]}}
Shell got {trace,<0.123.0>,call,{io,format,["Random: ~p~n",[502]]}}
ok
8> exit(PidToTrace).
** exception exit: <0.123.0>
9> dbg:stop_clear().
ok
10> 

换句话说,您只需在开始单元测试之前启动跟踪,测试跟踪消息,然后终止跟踪。确保您只跟踪发出调用的过程!,否则您将收到来自各地的消息。您可以在这里看到跟踪消息的样子:http://www.erlang.org/doc/man/erlang.html#trace-3

使用它,您还可以测试一些东西,比如流程选择正确的路径(例如调用您期望的正确函数),或者向其他进程发送正确的消息等等。在单元测试中,它常常被忽略,但它可能非常强大。不过,有一点是,它很快就会超越工程,要小心。

这可能不是公认的答案,但它有时是用于测试的一个好工具:)

祝好运。

票数 2
EN

Stack Overflow用户

发布于 2016-01-18 21:24:27

方法1:使用meck

经过测试的这段代码应该完全符合您的要求。它做了一些相当高级的meck技巧(特别是当它调用meck:passthrough/0时),但我认为它仍然非常清楚。

代码语言:javascript
复制
% 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)).

方法2:使用mock_io

最近(2017年5月),我编写了io,这是一种通过实现Erlang /O协议来模拟被测试单元的输入和输出的非常简单的方法。

使用mock_io时,等效代码如下:

代码语言:javascript
复制
% 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}).

还请注意,mock_io允许在UUT输入通道中注入数据,无论是stdin还是任何其他通道。例如:

代码语言:javascript
复制
% 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}).
票数 5
EN

Stack Overflow用户

发布于 2010-12-02 13:05:00

查看erlang: group /2,使用它您可以设置一个新的组领导,它将捕获发送的IO。

我知道eunit这样做也是为了捕获输出,这是在测试代码中完成的,所以它可能不太好,您必须尝试一下,看看会发生什么。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/4334420

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档