Mockito Como zombar e afirmar uma exceção lançada?

136

Estou usando mockito em um teste junit. Como você faz uma exceção acontecer e depois afirma que possui (pseudo-código genérico)

stackoverflow
fonte

Respostas:

75

Solução de estilo BDD (atualizada para Java 8)

Mockito sozinho não é a melhor solução para lidar com exceções, use Mockito com Catch-Exception

Mockito + Exceção de captura + AssertJ

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

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

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

Código de amostra

Dependências

MariuszS
fonte
2
O que é "exceção de captura"? Tem um link?
Duncan Jones
o que é caughtException?
Saif Masadeh
Entendi, está vindocom.googlecode.catchexception.CatchException.caughtException;
Saif Masadeh
212

Para responder sua segunda pergunta primeiro. Se você estiver usando o JUnit 4, poderá anotar seu teste com

@Test(expected=MyException.class)

afirmar que ocorreu uma exceção. E para "simular" uma exceção com o mockito, use

when(myMock.doSomething()).thenThrow(new MyException());
NilsH
fonte
2
essa abordagem é inaceitável quando você está testando o método de um objeto que possui algum estado. Por exemplo, existe um método de objeto que lança exceção se você chamar pela segunda vez. E você precisa testar para testar se ele lança exceção durante a segunda chamada de método, não a primeira. Se lançar MyException durante a primeira chamada de método (no estágio de preparação), deverá falhar no teste. Mas com essa abordagem, não podemos verificar durante qual chamada de método a exceção é lançada.
Sneg
Embora neste caso possamos capturar a exceção da primeira chamada de método e envolvê-la em RuntimeException.
Sneg
29

Se você quiser testar a mensagem de exceção, poderá usar o ExpectedException do JUnit com o 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"));
}
Daniel Treiber
fonte
given()De onde vem isto?
Mohammad Faisal
Também prefiro usar o @Rule, pois dessa maneira posso testar a mensagem ou causa esperada ou outras coisas relacionadas à exceção. Para verificar a causa da exceção, eu uso: expectableException.expectCause (Mockito.sameInstance (pectedException)) oupectedException.expectCause (Mockito.instanceOf (MyException.class)) e alguns outros que são úteis.
Crenguta S
19

Resposta atualizada para 19/06/2015 (se você estiver usando o java 8)

Basta usar assertj

Usando assertj-core-3.0.0 + Java 8 Lambdas

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

Referência: http://blog.codeleak.pl/2015/04/junit-testing-exceptions-with-java-8.html

Selwyn
fonte
funcionou para mim ... Também podemos verificar a mensagem de exceção também.assertThatThrownBy (() -> myService.sumTingWong ("badArg")). hasMessage ("test").
Sritam Jagadev 5/11/19
17

Se você estiver usando o JUnit 4 e o Mockito 1.10.x, anote seu método de teste com:

@Test(expected = AnyException.class)

e para lançar a exceção desejada, use:

Mockito.doThrow(new AnyException()).when(obj).callAnyMethod();
Prashant Kumar
fonte
16

Faça a exceção acontecer assim:

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

Verifique se isso aconteceu afirmando que seu teste gerará uma exceção:

@Test(expected = AnException.class)

Ou pela verificação simulada normal:

verify(obj).someMethod();

A última opção é necessária se o seu teste for projetado para provar que o código intermediário lida com a exceção (ou seja, a exceção não será lançada do seu método de teste).

Duncan Jones
fonte
A verifychamada afirma a exceção?
NilsH
@NilsH Não. Mas desde que a whencláusula esteja correta, ela deve ter gerado uma exceção.
Duncan Jones
10

Use o doThrow do Mockito e, em seguida, capture a exceção desejada para afirmar que foi lançada mais tarde.

@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);
}
Rodrigo Martins de Oliveira
fonte
5

Usando o mockito, você pode fazer a exceção acontecer.

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

Usando o Junit5, você pode afirmar a exceção, afirma se essa exceção é lançada quando o método de teste é chamado.

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

Encontre uma amostra aqui: assert exception junit

Anupama Boorlagadda
fonte
Obrigado ! Trabalhou para mim #
287
1

Não relacionado ao mockito, pode-se capturar a exceção e afirmar suas propriedades. Para verificar se a exceção ocorreu, afirme uma condição falsa no bloco try após a instrução que lança a exceção.

enguia ghEEz
fonte
@MariuszS resposta responde corretamente o que você está dizendo não tem relação com Mockito
pringi
@pringi Obrigado, vejo que a pergunta dizia respeito a zombar de uma exceção e capturá-la. Gostaria de saber se isso depende de qualquer comportamento do código em teste.
precisa saber é o seguinte
1

Ou se sua exceção for lançada do construtor de uma classe:

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

}
JediCate
fonte
-1

Afirme por mensagem de exceção:

    try {
        MyAgent.getNameByNode("d");
    } catch (Exception e) {
        Assert.assertEquals("Failed to fetch data.", e.getMessage());
    }
Sam
fonte
Se escrito dessa maneira, quando não houver exceção, o teste ainda passará . Qual é o que queremos evitar em primeiro lugar
Christian Lim