3

我开始common test在 erlang 中用作我的测试框架。

假设我有一个函数,我希望只接受正数,并且在任何其他情况下它都会崩溃。

positive_number(X) when > 0 -> {positive, X}.

我想测试一下

positive_number(-5).

不会成功完成。

我如何测试这种行为?在其他语言中,我会说测试用例预计会出现一些错误或异常,并且如果它在测试中的功能不会为无效的无效参数抛出任何错误,则会失败。如何通过普通测试做到这一点?

更新:

我可以让它工作

test_credit_invalid_input(_) ->
  InvalidArgument = -1,
  try mypackage:positive_number(InvalidArgument) of
    _ -> ct:fail(not_failing_as_expected)
  catch
    error:function_clause -> ok
  end.

但我认为这太冗长了,我想要类似的东西:

assert_error(mypackage:positive_number, [-1], error:function_clause)

我假设通用测试在某个地方有这个,并且我对框架缺乏适当的了解,这使我采取了如此冗长的解决方案。

更新: 受迈克尔回应的启发,我创建了以下函数:

assert_fail(Fun, Args, ExceptionType, ExceptionValue, Reason) ->
  try apply(Fun, Args) of
    _ -> ct:fail(Reason)
  catch
    ExceptionType:ExceptionValue -> ok
  end.

我的测试变成了:

test_credit_invalid_input(_) ->
  InvalidArgument = -1,
  assert_fail(fun mypackage:positive_number/1,
              [InvalidArgument],
              error,
              function_clause,
              failed_to_catch_invalid_argument).

但我认为它只是有效的,因为assert_fail调用比try....catch在每个测试用例中都更具可读性。

我仍然认为 Common Test 中应该存在一些更好的实现,IMO 在每个项目中重复实现这个测试模式是一种丑陋的重复。

4

1 回答 1

3

将异常转换为表达式并匹配它:

test_credit_invalid_input(_) ->
    InvalidArgument = -1,
    {'EXIT', {function_clause, _}}
            = (catch mypackage:positive_number(InvalidArgument)).

这会将您的异常变成非异常,反之亦然,这可能与您期望的一样简洁。

您也可以始终使用宏或函数来隐藏详细程度。

编辑(回复:评论):

如果您使用完整的 try catch 构造,如您的问题所示,您将丢失有关任何失败情况的信息,因为该信息被丢弃,有利于原子“not_failing_as_expected”或“failed_to_catch_invalid_argument”。

在 shell 上尝试预期的失败值:

1> {'EXIT', {function_clause, _}}
        = (catch mypackage:positive_number(-4)).             
{'EXIT',{function_clause,[{mypackage,positive_number,
                                 [-4],
                                 [{file,"mypackage.erl"},{line,7}]},
                      {erl_eval,do_apply,6,[{file,"erl_eval.erl"},{line,661}]},
                      {erl_eval,expr,5,[{file,"erl_eval.erl"},{line,434}]},
                      {erl_eval,expr,5,[{file,"erl_eval.erl"},{line,441}]},
                      {shell,exprs,7,[{file,"shell.erl"},{line,676}]},
                      {shell,eval_exprs,7,[{file,"shell.erl"},{line,631}]},
                      {shell,eval_loop,3,[{file,"shell.erl"},{line,616}]}]}}

在 shell 上验证预期的成功值:

2> {'EXIT', {function_clause, _}} = (catch mypackage:positive_number(3)). 
** exception error: no match of right hand side value {positive,3}

在这两种情况下,您都会获得大量信息,但至关重要的是,您可以从这两种情况中了解用于调用您的测试函数的参数(尽管在第二种情况下,这只是因为您的函数是确定的并且是一对一的)。

在像这样的简单情况下,这些事情并不重要,但作为原则,这很重要,因为稍后在更复杂的情况下,可能使您的函数失败的值没有像这里那样硬编码,您可以不知道什么值导致你的函数失败,或者什么返回值。这可能是查看一两分钟丑陋的回溯并准确了解问题所在,或者花费 15 分钟设置测试以真正找出发生了什么之间的区别......或者,更糟糕的是,如果它是heisenbug你可能要花几个小时寻找它!

于 2015-11-15T21:37:23.257 回答