Mockito проверяют после исключения Junit 4.10

Я тестирую метод с ожидаемым исключением. Мне также нужно убедиться, что какой-то код очистки был вызван (для смоделированного объекта) после создания исключения, но похоже, что проверка игнорируется. Вот код Я использую джунит ExpectedExceptionRule проверить ожидаемое исключение.

@Rule
public ExpectedException expectedEx = ExpectedException.none();

@Test
public void testExpectedException()
{
   MockedObject mockObj = mock(MockedObj.class);
   MySubject subject = new MySubject(mockedObj);
   expectedEx.expect(MyException.class);
   expectedEx.expectMessage("My exception message.");
   subject.someMethodThrowingException();
   verify(mockObj).
       someCleanup(eq(...));
}

Кажется, что verify полностью игнорируется. Независимо от того, какой метод я положил в verifyмой тест проходит, что не то, что я хочу.

Есть идеи, почему это происходит?

3 ответа

Решение

ExpectedException работает, оборачивая весь ваш тестовый метод в блок try-catch через JUnit @Rule. Когда ваш код генерирует исключение, он поднимается по стеку до ближайшей попытки / уловки, что происходит в экземпляре ExpectedException (который проверяет, что это ожидаемое вами исключение).

В Java, если в методе возникает неперехваченное исключение, элемент управления никогда не вернется к операторам позже в этом методе. Здесь применяются те же правила: Control никогда не возвращается к операторам в вашем тесте после исключения.

Технически, вы можете поместить проверки в окончательный блок, но это, как правило, плохая привычка. РЕДАКТИРОВАТЬ: ваша тестируемая система может выдать непредвиденное исключение или вообще никакого исключения, которое даст вам полезное сообщение об ошибке и трассировку; однако, если этот сбой приведет к тому, что ваши проверки или утверждения потерпят неудачу в finally блок, то Java покажет это, а не сообщение о неожиданном исключении или неожиданном успехе. Это может затруднить отладку, особенно из-за того, что ваша ошибка возникнет из строк кода, следующих за основной причиной ошибки, что неверно означает, что приведенный выше код завершился успешно.

Если вам действительно нужно проверить состояние после исключения, для каждого метода, вы всегда можете вернуться к этой идиоме:

@Test
public void testExpectedException()
{
  MockedObject mockObj = mock(MockedObj.class);
  MySubject subject = new MySubject(mockedObj);
  try {
    subject.someMethodThrowingException();
    fail("Expected MyException.");
  } catch (MyException expected) {
    assertEquals("My exception message.", expected.getMessage());
  }
  verify(mockObj).someCleanup(eq(...));
}

Обновление: с помощью лямбда-выражений Java 8 вы можете заключить вызов функционального интерфейса в блок try достаточно кратко, чтобы быть полезным. Я полагаю, что поддержка этого синтаксиса найдет свое отражение во многих стандартных библиотеках тестирования.

assertThrows(MyException.class,
    () -> systemUnderTest.throwingMethod());

Как только исключение выдается в UT, весь приведенный ниже код будет игнорироваться.

@Test(expected = Exception.class)
public void testExpectedException() {
   MockedObject mockObj = mock(MockedObj.class);
   MySubject subject = new MySubject(mockedObj);
   subject.doSomething(); // If this line results in an exception then all the code below this will be ignored.
   subject.someMethodThrowingException();
   verify(mockObj).
       someCleanup(eq(...));
}

Чтобы противостоять этому и проверить все сделанные вызовы, мы можем использовать try with finally.

@Test(expected = Exception.class)
    public void testExpectedException() {
          MockedObject mockObj = mock(MockedObj.class);
          MySubject subject = new MySubject(mockedObj);
          try {
               subject.someMethodThrowingException(); 
          } finally {
             verify(mockObj).
             someCleanup(eq(...));
          }
} 

Более элегантное решение с исключением catch

@Test
public void testExpectedException()
{
    MockedObject mockObj = mock(MockedObject.class);
    MySubject subject = new MySubject(mockObj);

    when(subject).someMethodThrowingException();

    then(caughtException())
            .isInstanceOf(MyException.class)
            .hasMessage("My exception message.");

    verify(mockObj).someCleanup(eq(...));
}

Я еще не пробовал этого, но в дополнение к превосходному ответу Джеффа Боумена у вас может быть выбор использовать правило ExpectedException с конструкцией try... finally, поместив оператор проверки в блок finally.

Другие вопросы по тегам