2015-11-15 5 views
2

Я начинаю использовать common test как свою тестовую структуру в erlang.Как проверить обработку ошибок в общем тесте?

Предположим, что у меня есть функция, которую я ожидаю принять только положительные числа, и она должна ударить в любом другом случае.

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

и я хочу, чтобы проверить, что

positive_number(-5). 

не будет успешно завершена.

Как проверить это поведение? В других языках я бы сказал, что тестовый пример ожидает некоторой ошибки или исключения и терпит неудачу, если он работает под тестом, не вызывает ошибок для недопустимого недопустимого параметра. Как это сделать с общим тестом?

Update:

я могу заставить его работать с

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) 

I при условии, что общий тест имеет это в некотором месте, и мне не хватает надлежащего знания структуры, которая заставляет меня принимать такое подробное решение.

Update: Вдохновленный ответ Майкла я создал следующую функцию:

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 - это уродливое повторение, чтобы этот тестовый шаблон неоднократно реализовывался в каждом проекте.

ответ

2

Преобразовать исключение выражения и сопоставить его:

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

Это превращает ваш исключение в не исключение, и наоборот, в вероятно, столь же лаконичным способом, как вы можете ожидать.

Вы всегда можете использовать макрос или функцию, чтобы скрыть многословие.

Edit (вос: комментарий):

Если вы используете полную попытку поймать конструкцию, как и в вашем вопросе, вы теряете информацию о любом случае неудач, потому что информация выбрасывается в пользу атома 'not_failing_as_expected' или 'failed_to_catch_invalid_argument'.

Попытка ожидаемый провал значение на раковине:

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}]}]}} 

Центровочный ожидаемое значение успеха на раковине:

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

В обоих случаях вы получите много информации, но самое главное, от оба из них вы можете определить, какие параметры использовались для вызова вашей тестируемой функции (хотя во втором случае это только потому, что ваша функция определена и одна к одной).

В таком простом случае эти вещи не имеют большого значения, но в принципе, это важно, потому что позже с более сложными случаями, где, возможно, значение, которое не выполняет вашу функцию, не является жестко закодированным, поскольку оно здесь вы можете не знать, какое значение заставило вашу функцию терпеть неудачу, или какое возвращаемое значение. Тогда это может означать разницу между взглядом на уродливую обратную линию на мгновение или на то, чтобы понять, в чем проблема, или потратить 15 минут на установление теста, чтобы действительно узнать, что происходит ... ИЛИ, что еще хуже, если это heisenbug вы можете потратить часы, чтобы найти его!

+0

Это решение работает, но дает более запутанное сообщение об ошибке при неудачах тестирования моего предыдущего решения. Мне понравилась идея вспомогательной функции. –

+0

@JonasFagundes Вы можете включить макросы EUnit и использовать их только под общим тестом. –

+0

@JonasFagundes Действительно ли запутанное сообщение об ошибке имеет значение? Я знаю, что трассировка стека Erlang не особенно хороша, но главным образом потому, что Erlang функционирует, вы будете знать, какая функция не удалась, где и почему из нее ... где, как если бы вы использовали try catch, как в вашем вопросе, вы фактически выбрасываете все эту информацию и заменить ее «not_failing_as_expected». Я понимаю ваше желание сделать это до некоторой степени, но я думаю, что это действительно неправильный путь. Я добавлю это к ответу, чтобы было ясно, не могу сделать это в комментарии на самом деле. – Michael

Смежные вопросы