web-dev-qa-db-fra.com

Essayez catch dans un test JUnit

J'écris des tests unitaires pour une application qui existe déjà depuis longtemps. Certaines des méthodes que j'ai besoin de tester sont construites comme ceci:

public void someMethod() throws Exception { 
   //do something 
}

Si je veux tester ces méthodes, je dois écrire quelque chose comme ceci dans mon test unitaire:

@Test
public void someTest() {
   try {
      someMethod();
   }
   catch (Exception e) {
      e.printStackTrace();
   }
}

Est-ce une bonne pratique de faire cela? Ou existe-t-il un autre moyen de tester ces méthodes?

J'ai fait quelques recherches sur Internet et j'ai trouvé quelques solutions avec l'annotation @Rule et @Test(expected=Exception.class), mais cela ne fonctionne pas (Eclipse continue d'afficher la ligne someMethod() dans le test comme étant incorrecte) . Je ne sais pas si elles sont bonnes solutions, parce que je suis assez nouveau dans toute l'histoire de tests unitaires.

Si quelqu'un qui en sait beaucoup pourrait m'aider, je serais vraiment reconnaissant.

46
Nelsch

Exception étant une exception vérifiée, vous pouvez soit:

  • Avoir à intercepter l'exception dans une instruction try...catch, ou
  • Déclarez l'exception à lancer dans la méthode elle-même.

Ce que vous avez là-haut fonctionne très bien, mais ma préférence personnelle est de déclarer l'exception levée. De cette façon, si une exception que je ne m'attendais pas est levée lors de l'exécution du test, le test fail.

@Test
public void someTest() throws Exception {
    // dodgy code here
}

Si nous avons besoin de voir si une exception spécifique est levée, vous avez la possibilité d'utiliser @Rule ou d'ajouter directement la valeur à l'annotation @Test.

@Test(expected = FileNotFoundException.class)
public void someTest() throws Exception {
    // dodgy code here
}

Dans JUnit 5, vous pouvez utiliser Assertions.expectThrows pour accomplir la même chose. Je suis moins familier avec cette approche car ce n'est pas encore GA au moment de l'édition, mais cela semble accepter une Executable provenant de JUnit 5.

@Test
public void someTest() {
    assertThrows(FileNotFoundException.class, () ->
         { dodgyService.breakableMethod() };
}
61
Makoto
@Test
public void someTest() {
   try {
     someMethod();
   }
   catch (Exception e) {
     Assert.fail("Exception " + e);
   }
}

Est-ce que vous pouvez faire, si l'exception ne devrait pas se produire. Une alternative serait de jeter l’exception dans la signature comme ceci:

@Test
public void someTest() throws Exception {
     someMethod();
}

La différence est que, dans un cas, le test échouera avec une exception d'assertion et dans l'autre cas, il échouera parce que le test s'est écrasé. (comme quelque part dans votre code, vous obtenez un NPE et le test le fera à cause de cela)

Vous devez le faire car Exception est une exception vérifiée. Voir Exception cochée ou non cochée

Le @Test (attendu = Exception.class) est destiné aux tests, qui veulent vérifier que l'exception sera levée. 

@Test(expected=ArrayIndexOutOfBounds.class)
public void testIndex() {
   int[] array = new int[0];
   int var = array[0]; //exception will be thrown here, but test will be green, because we expect this exception

}
22
morpheus05

Ne saisissez pas l'exception de votre application dans votre code de test. Au lieu de cela, déclarez qu'il soit projeté vers le haut.

Parce que, lorsque TestRunner de JUnit trouve une exception levée, il la consigne automatiquement en tant que error pour le testcase.

Si testcase s'attend à ce que la méthode lève une Exception, vous devez utiliser @Test(expected=Exception.class) ou intercepter l'exception.

Dans d'autres cas, il suffit de le jeter vers le haut avec,

public void someTest() throws Exception {
6
Codebender

Vous pouvez ajouter une exception à la signature de la méthode de test. Ensuite, si vous testez si une exception est levée, vous devez utiliser @Test(expected=Exception.class). Dans les cas de test où aucune exception ne doit être levée, le test réussira. 

@Test
public void testCaseWhereExceptionWontBeThrown() throws Exception {
    someMethod(); //Test pass
}

@Test(expected = Exception.class)
public void testCaseWhereExceptionWillBeThrown() throws Exception {
    someMethod(); //Test pass
}
6
Héctor

Il existe deux règles principales sur la façon de traiter les exceptions chez les testeurs Junit:

  1. Si l'exception a été créée dans le code testé:

    • Si cela était prévu, déclarez-le dans l'attribut expected de l'annotation Test. Ou, si des vérifications supplémentaires doivent être effectuées sur l'objet exception lui-même, récupérez-le et ignorez-le. (Dans ce cas, il faut également appeler Assert.fail à la fin du bloc try pour indiquer que l'exception attendue n'a pas été produite).
    • Si ce n'était pas prévu, récupérez-le et exécutez Assert.fail. (Un appel précédent à Exception.printStackTrace est également utile).
  2. Si l'exception ne provient pas du code testé ou n'est pas intéressante pour le test (par exemple, la plupart des exceptions IOExceptions sont générées au niveau du réseau, avant que le test ne puisse même être complété), relancez-le à la throws clause.

  3. </ p></ li>

Pourquoi devriez-vous vous attendre à une exception dans le testeur? Rappel: Vous devez coder une méthode de test pour chaque résultat possible sur le code testé (afin d'obtenir une couverture de code élevée): Dans votre cas, une méthode qui doit être renvoyée avec succès, et au moins une autre qui doit produire une exception.

3
Little Santi

Trois points à propos de JUnit:

  • Les tests doivent être précis, ils doivent réussir ou échouer sans ambiguïté en se basant uniquement sur la manière dont les entrées de test sont configurées.

  • Les tests doivent avoir des échecs rapportés dans la structure.

  • Les tests ne doivent pas reposer sur la lecture de leur sortie.

Votre exemple échoue sur les trois points. Si une exception est levée ou non, le test réussit quand même. Si une exception est levée, JUnit ne le découvre jamais et ne peut pas l'inclure dans les résultats du test. La seule façon de savoir que quelque chose a mal tourné est de lire ce que le test écrit sur stdout, ce qui rend les erreurs trop faciles à ignorer. Ce n'est pas un moyen utile d'écrire des tests.

JUnit a été conçu pour faciliter la tâche et donner aux développeurs des informations utiles. Si une exception est générée par une méthode de test, elle est interceptée par la structure. Si le test a été annoté avec une exception indiquant que cette exception est attendue, le cadre marque le test comme ayant réussi. Sinon, la structure échoue au test et enregistre le stacktrace pour la génération de rapports. La structure indique quelles assertions échouent et quelles exceptions inattendues se sont produites afin que tout le monde sache si les tests ont fonctionné ou non. 

Si vous vous attendez à ce qu'un test réussisse sans générer d'exception, si l'un des éléments du test peut générer une exception vérifiée, ajoutez throws Exception à la signature de la méthode de test. L'ajout de throws à la signature ne dit pas que la méthode doit jette quoi que ce soit, elle permet simplement que toutes les exceptions qui se produisent se produisent afin que le cadre de test puisse les détecter.

Le seul cas où vous captureriez l'exception dans le test est celui dans lequel vous souhaitez tester les assertions relatives à l'exception; Par exemple, vous pouvez vérifier que le message sur l'exception correspond à ce que vous attendez, ou si une cause est définie pour l'exception. Dans ce cas, vous ajouteriez Assert.fail() à la fin du bloc try, de sorte que le fait de ne pas déclencher d'exception provoque l'échec du test.

Lorsque vous écrivez un test au début, faites-le échouer. De cette façon, vous vous prouvez que vous savez ce que fait le test et vous confirmez qu’en cas d’échec, vous en serez averti.

Quel genre d'exception est-ce? Est-ce

  1. une exception de faire quelque chose comme utiliser des flux qui ne se produira pas dans votre test unitaire ou
  2. une exception qui peut arriver à cause d'une sorte de mauvaise entrée?

Si c'est 1. Je voudrais simplement le mettre au niveau de la signature de la méthode car un try-catch ne sert à rien d'autre qu'à la cérémonie.

@Test
public void testFoo() throws Exception {
    // ...
}

Si c'est 2. ça devient un peu plus compliqué. Vous devez vous demander ce qui devrait se passer si l'exception est levée. Le test doit-il échouer? Est-ce prévu? Est-ce que ce n'est pas pertinent? Exemples ci-dessous de la façon de gérer tous ces éléments. ATTENTION: J'ai seulement utilisé Exception parce que vous l'avez fait. J'espère que ce n'est vraiment pas le cas, car s'il est possible qu'une exception autre soit renvoyée autrement que prévu, elle sera très sournoise. Si possible, n'utilisez pas Exception, utilisez quelque chose de plus spécifique (dans le code junit et).

// The below code assumes you've imported the org.junit.Assert class.

@Test
public void thisShouldFailIfExceptionCaught() {
    //Given...
    try {
        // When...
    } catch (Exception e) {
        Assert.fail();
    }
    // Then...
}

@Test
public void thisShouldPassOnlyIfTheExceptionIsCaught() {
    //Given...
    try {
        // When...
        Assert.fail();
    } catch (Exception expected) {}
    // No "then" needed, the fact that it didn't fail is enough.
}

@Test
public void irrelevantExceptionThatCouldBeThrown() {
    //Given...
    try {
        // When...
    } catch (Exception e) {}
    // Then...
}
0
Captain Man