在Erlang的EUnit中,Assert函数是用匿名函数调用的

cbwuti44  于 2022-12-08  发布在  Erlang
关注(0)|答案(2)|浏览(188)

I have some code that looks like this:

-module(ca_data).
-export([delete_ca_data/1]).

% ...

delete_ca_data(N) when N < 1 -> ?NEGATIVE_ID_ERROR;
delete_ca_data(N) -> util_db_generic:delete_object(ca_data, N, fun(_) -> ok end).

% ...

And I have some test code that looks like this:

wrap_meck(MockMod, MockArgs, F) ->
    meck:new(MockMod, MockArgs),
    try
        F()
    catch Ex ->
        throw(Ex)
    after
        meck:unload(MockMod)
    end.

delete_ca_data_test() ->
    F = fun() ->
        meck:expect(util_db_generic, delete_object, fun (_, _, _) -> ok end),

        ?assertEqual(?NEGATIVE_ID_ERROR, ca_data:delete_ca_data(-1)),

        ?assertEqual([], meck:history(util_db_generic)),

        meck:expect(util_db_generic, delete_object, fun(_, _, _) -> ok end),
        ?assertEqual(ok, ca_data:delete_ca_data(1)),

        ?assertEqual(
            [{self(), {util_db_generic, delete_object, [ca_data, 1, fun(_) -> ok end]}, ok}], % Uh-oh
            meck:history(util_db_generic)
        )
    end,
    wrap_meck(util_db_generic, [], F).

Unfortunately, the util_db_generic:delete_object function is called with a function created in the module under test.
This provides some issues when trying to assert the history of the function calls (for example, on the line marked "Uh-oh"). Since this function is created in-situ (and the memory pointer is effectively random), it's challenging to assert what this function should look like. This code does not result in any compilation error. However, the asserts will never return as valid, since there are two different functions being asserted against each other (one created in the ca_data module, and one in the testcase). I tried using ?assertMatch and changing the fun(_) -> ok end 's out for _ s, but I am getting this compilation error on the assert line:

illegal pattern

How can I match these results?

bvhaajcl

bvhaajcl1#

您想要的判断提示可能如下所示:

?assertMatch(
    [{Self, {util_db_generic, delete_object, [ca_data, 1, Fun]}, ok}] when Self == self() andalso is_function(Fun, 1), % Yay?
    meck:history(util_db_generic)
)

这与?assertMatch文档相匹配,因为您可以在case语句中编写相同的内容,如下所示:

case meck:history(util_db_generic) of
    [{Self, {util_db_generic, delete_object, [ca_data, 1, Fun]}, ok}] when Self == self() andalso is_function(Fun, 1) ->
        true;
    _ ->
        false
end
  • (注意,case语句可以更短,在保护之间使用,,但在宏中不起作用,因此此处我们必须使用andalso)*

必须首先将pid与Self匹配,然后在保护中检查它的原因是,在匹配表达式中不允许函数调用:

1> [self()] = [self()].
* 1:2: illegal pattern

因此,你必须先绑定它,然后检查guard中的值。当然,正如你上面所写的,在表达式之外绑定Self也是有效的。这只是一个品味和/或可读性的问题(事实上,我认为先绑定Self更清楚)。

ljo96ir5

ljo96ir52#

虽然Erlang文档通常很好,但这一点让用户失望。assertMatch文档声明:

Evaluates Expr and matches the result against GuardedPattern, if testing is enabled. ... GuardedPattern can be anything that you can write on the left hand side of the -> symbol in a case-clause, except that it cannot contain comma-separated guard tests.

self()的文档说明:

Allowed in guard tests.

然而,self()函数不能用于assertMatch宏。我不清楚为什么,但实验已经证明了这一点。
编辑1:
当我把测试代码编辑成这样的时候,情节就复杂了:

delete_ca_data_test() ->
    F = fun() ->
        meck:expect(util_db_generic, delete_object, fun (_, _, _) -> ok end),

        ?assertEqual(?NEGATIVE_ID_ERROR, ca_data:delete_ca_data(-1)),

        ?assertEqual([], meck:history(util_db_generic)),

        meck:expect(util_db_generic, delete_object, fun(_, _, _) -> ok end),
        ?assertEqual(ok, ca_data:delete_ca_data(1)),

        Self = self(),

        ?assertEqual(
            [{Self, {util_db_generic, delete_object, [ca_data, 1, _]}, ok}], % Uh-oh
            meck:history(util_db_generic)
        )
    end,
    wrap_meck(util_db_generic, [], F).

代码正确编译和运行!2对我来说,这是一个令人惊讶的结果!3函数(应该可以在守卫中使用)是不允许的,但是函数的返回是!4有趣!

相关问题