web-dev-qa-db-fra.com

Mockito vérifier après exception Junit 4.10

Je teste une méthode avec une exception attendue. J'ai également besoin de vérifier qu'un code de nettoyage a été appelé (sur un objet simulé) après la levée de l'exception, mais il semble que cette vérification soit ignorée. Voici le code. J'utilise Junit ExpectedExceptionRule pour vérifier l'exception attendue.

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

Il semble que le verify soit totalement ignoré. Peu importe la méthode que je mets dans le verify, mon test passe, ce qui n'est pas ce que je veux.

Une idée pourquoi c'est arrivé?

62
Eqbal

ExpectedException fonctionne par encapsulant l'intégralité de votre méthode de test dans un bloc try-catch via un JUnit @Rule . Lorsque votre code lève une exception, il remonte la pile jusqu'à la tentative/capture la plus proche, qui se trouve être dans l'instance ExpectedException (qui vérifie que c'est l'exception que vous attendez).

En Java, si une exception non interceptée se produit dans une méthode, le contrôle ne reviendra jamais aux instructions plus tard dans cette méthode. Les mêmes règles s'appliquent ici: Le contrôle ne revient jamais aux instructions de votre test après l'exception.

Techniquement, vous pourriez mettre les vérifications dans un bloc enfin, mais cela a tendance à être ne mauvaise habitude . EDIT: Votre système en cours de test peut lever une exception inattendue, ou aucune exception, ce qui vous donnerait un message d'échec et une trace utiles; cependant, si cet échec entraîne l'échec de vos vérifications ou assertions dans le bloc finally, alors Java affichera cela plutôt qu'un message sur l'exception inattendue ou le succès inattendu. peut rendre le débogage difficile, en particulier parce que votre erreur proviendra de lignes de code suivant la cause racine de l'erreur, ce qui implique incorrectement que le code au-dessus a réussi.

Si vous avez vraiment besoin de vérifier l'état après l'exception, par méthode, vous pouvez toujours revenir à cet idiome:

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

Mise à jour: Avec Java 8, vous pouvez encapsuler un appel d'interface fonctionnelle dans un bloc try assez concis pour être utile . J'imagine que la prise en charge de cette syntaxe se retrouvera dans de nombreuses bibliothèques de tests standard.

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

Solution plus élégante avec 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(...));
}
5
MariuszS

Je n'ai pas encore essayé cela, mais en plus de l'excellente réponse de Jeff Bowman, vous pourriez avoir le choix d'utiliser la règle ExpectedException avec une construction try ... finally, en plaçant votre instruction de vérification dans le bloc finally.

4
Kevin Welker

Une fois que l'exception est levée dans UT, tout le code ci-dessous sera ignoré.

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

Pour contrer cela et vérifier tous les appels effectués, nous pouvons utiliser essayez avec enfin.

@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
singh30