Mockito如何模拟和声明抛出的异常?


Answers:


75

BDD样式解决方案(已更新为Java 8)

Mockito并不是处理异常的最佳解决方案,请将MockitoCatch-Exception结合使用

Mockito + 捕获异常 + AssertJ

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

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

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

样例代码

依存关系


2
什么是“捕获例外”?有连结吗?
Duncan Jones

什么caughtException
赛义夫·马萨德

知道了,它来自com.googlecode.catchexception.CatchException.caughtException;
Saif Masadeh,

212

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

@Test(expected=MyException.class)

断言发生了异常。并使用嘲笑“模拟”异常,请使用

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

2
当您测试具有某种状态的对象的方法时,这种方法是不可接受的。例如,有一个对象方法会在您第二次调用时引发异常。并且您需要进行测试以测试它是否在第二个方法调用而不是第一个方法调用期间引发了异常。如果在第一个方法调用期间(在准备阶段)抛出MyException,则它应该无法通过测试。但是,使用这种方法,我们无法检查在哪个方法调用期间引发了异常。
斯内格

尽管在这种情况下,我们可以从第一个方法调用中捕获异常并将其包装在RuntimeException中。
斯内格

29

如果还要测试异常消息,则可以将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()这是哪里来的?
Mohammad Faisal


我也更喜欢使用@Rule,因为这样我可以测试预期的消息或原因或与异常有关的其他内容。为了检查异常原因,我使用了:ExpectedException.expectCause(Mockito.sameInstance(expectedException))或ExpectedException.expectCause(Mockito.instanceOf(MyException.class))和一些其他有用的东西。
Crenguta S

19

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

只需使用assertj

使用assertj-core-3.0.0 + Java 8 Lambdas

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

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


为我工作...此外,我们也可以检查异常消息。
Sritam Jagadev '19

17

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

@Test(expected = AnyException.class)

并抛出所需的异常使用:

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

16

像这样使异常发生:

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

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

@Test(expected = AnException.class)

或通过普通的模拟验证:

verify(obj).someMethod();

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


verify呼叫是否断言异常?
NilsH 2013年

@NilsH否。但是,如果该when子句正确,则必须抛出异常。
Duncan Jones

10

使用Mockito的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);
}

5

使用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());
}

在此处找到示例:断言异常junit


谢谢 !为我工作
HariKishore

1

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


@MariuszS回复正确回答了您所说的与Mockito无关的内容
pringi

@pringi谢谢,我看到这个问题与模拟异常和捕获异常有关。我不知道这是否取决于被测代码的任何行为。
鳗鱼ghEEz

1

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

@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);  

}

-1

通过异常消息声明:

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

如果这样编写,则在没有引发异常时,测试仍将通过。我们首先要避免的是
Christian Lim
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.