Mockito 如何模拟和断言抛出的异常?

Posted

技术标签:

【中文标题】Mockito 如何模拟和断言抛出的异常?【英文标题】:Mockito How to mock and assert a thrown exception? 【发布时间】:2013-04-21 01:11:06 【问题描述】:

我在 junit 测试中使用了 mockito。你如何让异常发生然后断言它有(通用伪代码)

【问题讨论】:

【参考方案1】:

先回答你的第二个问题。如果您使用的是 JUnit 4,则可以使用

注释您的测试
@Test(expected=MyException.class)

断言发生了异常。要使用 mockito “模拟”异常,请使用

when(myMock.doSomething()).thenThrow(new MyException());

【讨论】:

当您正在测试具有某种状态的对象的方法时,这种方法是不可接受的。例如,如果您第二次调用它,则有一个对象方法会引发异常。并且您需要测试它是否在第二次方法调用期间抛出异常,而不是第一次。如果它在第一个方法调用期间(在准备阶段)抛出 MyException,那么它应该无法通过测试。但是使用这种方法,我们无法检查在哪个方法调用期间引发了异常。 虽然在这种情况下我们可以从第一个方法调用中捕获异常并将其包装在 RuntimeException 中。 如果方法 doSomething() 返回类型为 void,这不起作用?【参考方案2】:

BDD 风格解决方案(更新到 Java 8)

Mockito 单独不是处理异常的最佳解决方案,使用 MockitoCatch-Exception

Mockito + Catch-Exception + AssertJ

given(otherServiceMock.bar()).willThrow(new MyException());

when(() -> myService.foo());

then(caughtException()).isInstanceOf(MyException.class);

示例代码

Mockito + Catch-Exception + Assertj full sample

依赖关系

eu.codearte.catch-exception:catch-exception:2.0 org.assertj:assertj-core:3.12.2

【讨论】:

什么是“捕获异常”?有链接吗? caughtException 是什么? 知道了,来自com.googlecode.catchexception.CatchException.caughtException;【参考方案3】:

如果您也想测试异常消息,您可以将 JUnit 的 ExpectedException 与 Mockito 一起使用:

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

@Test
public void testExceptionMessage() throws Exception 
    expectedException.expect(AnyException.class);
    expectedException.expectMessage("The expected message");

    given(foo.bar()).willThrow(new AnyException("The expected message"));

【讨论】:

given()这是哪里来的? 来自 mockito-core:static.javadoc.io/org.mockito/mockito-core/2.23.4/org/mockito/… 我也更喜欢使用@Rule,因为这样我可以测试预期的消息或原因或与异常有关的其他内容。为了检查异常的原因,我使用:expectedException.expectCause(Mockito.sameInstance(expectedException)) 或 expectedException.expectCause(Mockito.instanceOf(MyException.class)) 以及其他一些派上用场的方法。【参考方案4】:

2015 年 6 月 19 日的更新答案(如果您使用的是 java 8)

只需使用 assertj

使用 assertj-core-3.0.0 + Java 8 Lambda

@Test
public void shouldThrowIllegalArgumentExceptionWhenPassingBadArg() 
assertThatThrownBy(() -> myService.sumTingWong("badArg"))
                                  .isInstanceOf(IllegalArgumentException.class);

参考:http://blog.codeleak.pl/2015/04/junit-testing-exceptions-with-java-8.html

【讨论】:

为我工作...我们也可以检查异常消息。assertThatThrownBy(() -> myService.sumTingWong("badArg")).hasMessage("test") .isInstanceOf(IllegalArgumentException .class);【参考方案5】:

如果您使用的是 JUnit 4 和 Mockito 1.10.x 注释你的测试方法:

@Test(expected = AnyException.class)

并抛出你想要的异常使用:

Mockito.doThrow(new AnyException()).when(obj).callAnyMethod();

【讨论】:

【参考方案6】:

让异常发生如下:

when(obj.someMethod()).thenThrow(new AnException());

通过断言您的测试将引发此类异常来验证它是否已发生:

@Test(expected = AnException.class)

或者通过普通的模拟验证:

verify(obj).someMethod();

如果您的测试旨在证明中间代码处理异常(即不会从您的测试方法抛出异常),则需要后一个选项。

【讨论】:

verify 调用是否断言异常? @NilsH 没有。但是如果when 子句是正确的,它肯定抛出了异常。 如果方法 someMethod() 返回类型为 void,那么它就不能这样工作。有什么方法可以模拟 void 方法的抛出异常?【参考方案7】:

使用Mockito's doThrow,然后捕获所需的异常以断言它稍后被抛出。

@Test
public void fooShouldThrowMyException() 
    // given
    val myClass = new MyClass();
    val arg = mock(MyArgument.class);
    doThrow(MyException.class).when(arg).argMethod(any());
    Exception exception = null;

    // when
    try 
        myClass.foo(arg);
     catch (MyException t) 
        exception = t;
    

    // then
    assertNotNull(exception);

【讨论】:

【参考方案8】:

使用 mockito,您可以让异常发生。

when(testingClassObj.testSomeMethod).thenThrow(new CustomException());

使用Junit5,可以断言异常,断言调用测试方法时是否抛出那个异常。

@Test
@DisplayName("Test assert exception")
void testCustomException(TestInfo testInfo) 
    final ExpectCustomException expectEx = new ExpectCustomException();

     InvalidParameterCountException exception = assertThrows(InvalidParameterCountException.class, () -> 
            expectEx.constructErrorMessage("sample ","error");
        );
    assertEquals("Invalid parametercount: expected=3, passed=2", exception.getMessage());

在此处查找示例:assert exception junit

【讨论】:

谢谢!为我工作【参考方案9】:

我认为这应该适合你。

assertThrows(someException.class, ()-> mockedServiceReference.someMethod(param1,parme2,..));

【讨论】:

【参考方案10】:

或者如果你的异常是从类的构造函数中抛出的:

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

@Test
public void myTest()     

    exception.expect(MyException.class);
    CustomClass myClass= mock(CustomClass.class);
    doThrow(new MyException("constructor failed")).when(myClass);  


【讨论】:

【参考方案11】:

与 mockito 无关,可以捕获异常并断言其属性。要验证异常确实发生了,请在抛出异常的语句之后的 try 块中声明一个错误条件。

【讨论】:

@MariuszS 回复正确回答了您所说的与 Mockito 无关 @pringi 谢谢,我看到这个问题涉及模拟异常和捕获它。我想知道这是否取决于被测代码的任何行为。【参考方案12】:

通过异常消息断言:

    try 
        MyAgent.getNameByNode("d");
     catch (Exception e) 
        Assert.assertEquals("Failed to fetch data.", e.getMessage());
    

【讨论】:

这样写,当没有没有异常抛出时,测试还是通过。这是我们首先要避免的

以上是关于Mockito 如何模拟和断言抛出的异常?的主要内容,如果未能解决你的问题,请参考以下文章

mock单元测试 mockito实践

SpringBoot 单元测试利器——Mockito

学习单元测试 Mockito 一篇文章就够了

Mockito 抛出异常

使用 Mockito 从模拟中抛出已检查的异常

Mockito:参数的模拟服务抛出空指针异常