【问题标题】:Mockito verify after exception Junit 4.10异常Junit 4.10后的Mockito验证
【发布时间】:2012-10-24 19:51:17
【问题描述】:

我正在测试一个带有预期异常的方法。我还需要验证在抛出异常后(在模拟对象上)调用了一些清理代码,但看起来验证被忽略了。这是代码。我正在使用 Junit ExpectedException 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里放什么方法,我的测试都通过了,这不是我想要的。

知道为什么会这样吗?

【问题讨论】:

    标签: java junit mockito junit-rule


    【解决方案1】:

    一旦在 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(...));
    }
    

    为了解决这个问题并验证所有调用,我们可以使用 tryfinally

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

    【讨论】:

      【解决方案2】:

      ExpectedExceptionwrapping your entire test method 通过JUnit @Rule 在try-catch 块中工作。当您的代码抛出异常时,它会向上堆栈到最近的 try/catch,这恰好位于 ExpectedException 实例中(它会检查它是否是您所期望的异常)。

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

      从技术上讲,您可以将验证放在 finally 块中,但这往往是a bad habit编辑: 您的被测系统可能会抛出意外异常,或者根本没有异常,这将为您提供有用的失败消息和跟踪;但是,如果该失败导致您的验证或断言在 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 块 concisely enough to be useful 中。我想对这种语法的支持会在许多标准测试库中找到。

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

      【讨论】:

      • 为什么将验证放在finally 块中会吞下异常? finally 没有捕捉到异常,所以它会被传播到包装器。见凯文·韦尔克的回答;这很好用。
      • @StefanDeitmer 我很抱歉没有很好地表达它。如果您的测试通过,是的,它会正常工作。如果测试失败,它可能来自于没有抛出异常,或者抛出了错误的异常,或者答案正在谈论的后置条件状态失败。问题是验证失败等于throwing an exception in a finally block,这将使您无法查看和调试被测方法可能抛出的任何意外异常。我的意思是那个意外的例外。
      【解决方案3】:

      catch-exception 提供更优雅的解决方案

      @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. 的类
      • 这类似于 github.com/Codearte/catch-exception/blob/legacy/1.x/… ,但现在已弃用。试试 CatchException2 或 AssertJ
      【解决方案4】:

      我还没有尝试过,但是除了 Jeff Bowman 的出色回答之外,您还可以选择使用带有 try...finally 构造的 ExpectedException 规则,将您的验证语句放在 finally 块中。

      【讨论】:

      • 检查我的中间段落...我认为这会起作用,但会吞下 ExpectedException 消息。不过还是谢谢夸奖!
      • 对我来说终于可以正常工作了 - 执行验证并仍然检测到预期的异常和它的消息
      • 你们说“expectedException Rule with try...finally”是什么意思?两者如何混合?
      • @Carl 我已经更新了我的答案以更好地解释。简而言之,问题是在测试失败的情况下调试过程是什么,以及如果被测方法抛出意外异常或没有异常,这可能有多困难。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-24
      • 2021-06-15
      • 1970-01-01
      • 2016-08-19
      • 1970-01-01
      • 2011-07-25
      相关资源
      最近更新 更多