如何在常见测试中测试错误处理?

时间:2015-11-15 17:14:15

标签: erlang common-test

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

假设我有功能,我希望只接受正数,而且在任何其他情况下它应该会爆炸。

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)

我认为普通测试在某些地方有这个,而且我缺乏对框架的正确认识,这让我采取了这样一个冗长的解决方案。

更新 受Michael的回应启发,我创建了以下功能:

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在每个项目中重复实现这种测试模式是一个难看的重复。

1 个答案:

答案 0 :(得分:2)

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

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你可能要花几个小时寻找它!