Проверка поведения при тестировании метода, выдающего исключение

Недавно в обзоре кода коллега сказал: «В тестах не должно быть блоков try-catch». Он также указал мне на информацию о ExpectedException, и я смог использовать это, чтобы переписать некоторые из моих тестов, которые выполняли проверки данных исключения.

Однако я сталкиваюсь с ситуацией, когда я не уверен, что смогу устранить попытку-улов. Я пишу модульный тест для метода, который выдает исключение and и выполняет некоторое поведение, которое мне нужно проверить с помощью JMockit. Предположим, что тестируемый код содержит что-то вроде

try {
    ...code...
} catch (SomeException e) {
    statusMessage.send("Failure", <parameters with some data>);
    throw e;
}

Тест JUnit в настоящее время выглядит так

@Test
public void test() {
    ... set things up ...
    try {
        callTheMethod();
        fail("No exception thrown");
    } catch (Exception e) {
        assertEquals(SomeException.class, e.getClass());
    }
    new Verifications() { {
        statusMessage.send("Failure", <expected data>);
    } }
}

Я не могу найти хороший способ избавиться от try-catch. Лично я не вижу проблемы в том, чтобы он был там, но если я хотя бы не попытаюсь избавиться от него, я, скорее всего, заразлюсь от него мой коллега :) :) :)

fail и assertEquals не нужны. Если есть простой способ сказать JUnit/JMockit просто игнорировать любое исключение из callTheMethod() и продолжать, это было бы хорошо — я могу создать еще один тест для проверки исключения.

Есть ли какая-то функция JUnit или JMockit, которая позволила бы мне выполнить то, что я пытаюсь сделать?

[Примечание: отредактировано, чтобы прояснить, что поведение, которое я тестирую, является вызовом имитируемого объекта, а не вызовом статического метода. Это действительно не имеет отношения к вопросу.]


person ajb    schedule 18.11.2015    source источник
comment
Глупо заявлять, что в тестах не должно быть блоков try-catch. Скорее, тесты не должны перехватывать исключения, если это конкретное исключение не ожидается как часть этого теста.   -  person gknicker    schedule 18.11.2015
comment
@gknicker Вы могли бы прекратить свой комментарий после первых семи слов.   -  person ajb    schedule 18.11.2015
comment
Может быть, какое-то среднее: вместо assertEquals(.. вы могли бы потенциально поместить часть new Verification в блок catch, а затем throw e. Не стал бы беспокоиться лично, потому что вас интересует не только исключение, но и то, что происходит между ними, и это просто не стандартный ожидаемый тест исключения.   -  person zapl    schedule 18.11.2015


Ответы (4)


Ваш коллега может быть прав в общем смысле, использование try-catch для проверки обработки исключений менее желательно, чем использование предоставленных ExpectedException утилит. Однако я не думаю, что это правило применимо к вашему случаю; поймать исключение, чтобы вы могли проверить другие вещи, вполне разумно. Нет другого способа остановить распространение исключения, насколько я знаю.

person Rosa    schedule 18.11.2015

В настоящее время с простым JUnit вам нужно использовать try-catch, если вы хотите что-то проверить после того, как утверждение было выдано. С JUnit 4.13 (еще не выпущенным) вы можете использовать

expectThrows(Exception.class, () -> callTheMethod());
new Verifications() { {
    statusMessage.send("Failure", <expected data>);
} }

В качестве альтернативы вы можете использовать игнорирование FishbowlException.

ignoreException(() -> callTheMethod());
new Verifications() { {
    statusMessage.send("Failure", <expected data>);
} }

Полное раскрытие: я автор Fishbowl.

person Stefan Birkner    schedule 18.11.2015
comment
Прохладный! Это будет полезно. Не думаю, что у нас есть Fishbowl для производства, поэтому я с нетерпением жду выхода версии 4.13. - person ajb; 18.11.2015
comment
Вы можете просто скопировать метод ignoreException из Fishbowl, если у вас нет Fishbowl. - person Stefan Birkner; 18.11.2015

Основываясь на API, я бы сказал, что вы можете сделать что-то вроде:

class AjbUnitTest
{
    @Rule 
    public ExpectedException thrown = ExpectedException.none();

    @Test
    public void test()
    {
        ... set things up ...
        thrown.expect(SomeException.class)
        callTheMethod();  / Preumably this throws SomeException
        ...
    }
}
person dave    schedule 18.11.2015
comment
Но когда callTheMethod() выдает исключение, для теста все кончено. Если после этой строки есть Verifications, они не будут проверены. Я полагаю, что уже проверил это, и это поведение, которое я наблюдал. - person ajb; 18.11.2015
comment
Ох, ну ладно. Если вам нужно продолжать, вам нужен try .. catch. Я думаю, что правило разумно, но правила можно нарушать, если это оправдано. Я думаю, что это такая ситуация. - person dave; 18.11.2015

Для конкретных утверждений об исключении AssertJ имеет хороший API:

@Test
public void testException() {
   assertThatThrownBy(() -> { throw new Exception("error!") })
     .isInstanceOf(Exception.class)
     .hasMessageContaining("error");
}

По сути, вы можете «собрать» исключение, не нарушая выполнение теста.

person S.D.    schedule 18.11.2015
comment
Хотя вы, вероятно, правы насчет StatusMessage (и я использовал макет в реальном коде, я просто пытался быстро собрать упрощенный пример), это никоим образом не отвечает на вопрос. - person ajb; 18.11.2015
comment
Код @ajb, указанный в вопросе, считается частью вопроса. - person S.D.; 18.11.2015
comment
Я думаю, что в SO довольно хорошо установлено, что если вы хотите сказать что-то о коде спрашивающего, что не относится к заданному им вопросу, это должен быть комментарий, а не отдельный ответ. - person ajb; 18.11.2015