Mockito在例外Junit 4.10之后进行验证


72

我正在测试具有预期异常的方法。我还需要在引发异常后验证是否在某个模拟对象上调用了一些清理代码,但看起来该验证已被忽略。这是代码。我正在使用JunitExpectedException Rule验证预期的异常。

@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,我的测试都通过了,这不是我想要的。

知道为什么会这样吗?

Answers:


84

ExpectedException通过将整个测试方法通过JUnit @Rule包装在try-catch块中来工作。当您的代码引发异常时,它会将堆栈上移到最近的try / catch,恰好在ExpectedException实例中(该实例检查您所期望的异常)。

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

从技术上讲,您可以将验证放在最后一块,但这往往是个坏习惯编辑:您的被测系统可能会引发意外的异常,或者根本没有异常,这将为您提供有用的故障消息和跟踪;但是,如果该失败导致您的验证或断言在该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的lambda表达式,您可以将功能接口调用足够简洁地包装在try块中,以变得有用。我想对这种语法的支持将在许多标准测试库中找到。

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

为什么将验证放在一个finally块中会吞下异常?finally不会捕获异常,因此它将传播到包装器。参见凯文·韦尔克的回答;这很好。
Stefan Frye,

我对@StefanDeitmer的口齿不清表示歉意。如果您的测试通过,是的,它将正常运行。如果测试失败,则可能是因为没有抛出异常,或者抛出了错误的异常,或者答案所指的后置条件状态失败。问题在于,验证失败将导致在一个finally块中引发一个异常,这将使您无法查看和调试被测方法可能引发的任何意外异常。那个意想不到的例外是我的意思。
杰夫·鲍曼

6

一旦在UT中引发Exception,下面的所有代码将被忽略。

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

5

捕获例外的更优雅解决方案

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

嗨,您能谈谈什么then(caughtException())吗?我必须假设whenverify实际上是Mockito.whenMockito.verify但是您没有指定then.
Navigatron

就像这样github.com/Codearte/catch-exception/blob/legacy/1.x/…,但是现在不推荐使用。试试CatchException2或AssertJ
MariuszS

4

我还没有尝试过,但是除了杰夫·鲍曼(Jeff Bowman)的出色答案之外,您还可以选择使用ExpectedException规则进行try ... finally构造,将您的verify语句放置在finally块中。


检查我的中间段落...我认为可以,但是会吞下ExpectedException消息。谢谢你的夸奖!
杰夫·鲍曼

4
最后,工作正常,我-执行验证,仍然检测预期例外,它的消息
卡尔·普里切特

你们说“带try ... finally的expectedException规则”是什么意思?两者如何混合?
drakonli

@卡尔我已经更新了答案以更好地解释。简而言之,问题是在测试失败的情况下调试过程是什么,以及如果被测方法抛出意外异常或无异常,则可能很难做到这一点。
杰夫·鲍曼
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.