Mockito 在异常 Junit 4.10 之后进行验证

2022-08-31 12:54:05

我正在测试一个具有预期异常的方法。我还需要验证在抛出异常后是否调用了一些清理代码(在模拟对象上),但看起来该验证被忽略了。下面是代码。我正在使用 Junit 来验证预期的异常。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(...));
}

似乎完全被忽视了。无论我用什么方法,我的测试都通过了,这不是我想要的。verifyverify

任何想法为什么会发生这种情况?


答案 1

ExpectedException的工作原理是通过 JUnit @Rule整个测试方法包装在 try-catch 块中。当您的代码引发异常时,它会沿着堆栈向上移动到最接近的 try/catch,该实例恰好位于 ExpectedException 实例中(该实例检查它是否是您预期的异常)。

在 Java 中,如果某个方法中发生未捕获的异常,则控件将永远不会返回到该方法后面的语句。此处适用相同的规则:控件在发生异常后永远不会返回到测试中的语句。

从技术上讲,您可以将验证放在最终的块中,但这往往是一个坏习惯编辑:您的受测系统可能会引发意外异常,或者根本没有异常,这将为您提供有用的失败消息和跟踪;但是,如果该失败导致您的验证或断言在块中失败,则Java将显示该错误,而不是有关意外异常或意外成功的消息。这可能会使调试变得困难,特别是因为您的错误将来自错误根本原因后面的代码行,错误地暗示其上方的代码成功了。finally

如果您确实需要在异常之后验证状态,则基于每个方法,您始终可以恢复到此习语:

@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 的 lambda 表达式,您可以将函数接口调用包装在 try 块中,使其足够简洁,以致用。我想对这种语法的支持会进入许多标准测试库。

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

答案 2

在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 final。

@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(...));
          }
} 

推荐