web-dev-qa-db-fra.com

Mauvaise forme pour le test JUnit pour lever l'exception?

Je suis assez nouveau sur JUnit, et je ne sais pas vraiment quelles sont les meilleures pratiques pour les exceptions et la gestion des exceptions.

Par exemple, supposons que j'écris des tests pour une classe IPAddress. Il a un constructeur IPAddress (String addr) qui lèvera une InvalidIPAddressException si addr est null. Pour autant que je puisse en juger par googler, le test du paramètre null ressemblera à ceci.

@Test
public void testNullParameter()
{
    try
    {
        IPAddress addr = new IPAddress(null);
        assertTrue(addr.getOctets() == null);
    }
    catch(InvalidIPAddressException e)
    {
        return;
    }

    fail("InvalidIPAddressException not thrown.");
}

Dans ce cas, try/catch est logique car je sais que l'exception arrive.

Mais maintenant, si je veux écrire testValidIPAddress (), il y a deux façons de le faire:

Voie # 1:

@Test
public void testValidIPAddress() throws InvalidIPAddressException
{
    IPAddress addr = new IPAddress("127.0.0.1");
    byte[] octets = addr.getOctets();

    assertTrue(octets[0] == 127);
    assertTrue(octets[1] == 0);
    assertTrue(octets[2] == 0);
    assertTrue(octets[3] == 1);
}

Voie # 2:

@Test
public void testValidIPAddress()
{
    try
    {
        IPAddress addr = new IPAddress("127.0.0.1");
        byte[] octets = addr.getOctets();

        assertTrue(octets[0] == 127);
        assertTrue(octets[1] == 0);
        assertTrue(octets[2] == 0);
        assertTrue(octets[3] == 1);
    }
    catch (InvalidIPAddressException e)
    {
        fail("InvalidIPAddressException: " + e.getMessage());
    }
}

Est-il courant de lever des exceptions inattendues à JUnit ou de les gérer vous-même?

Merci pour l'aide.

56
Seth

En fait, l'ancien style de test d'exception consiste à encapsuler un bloc try autour du code qui lève l'exception, puis à ajouter une instruction fail() à la fin du bloc try. Quelque chose comme ça:

public void testNullParameter() {
    try {
        IPAddress addr = new IPAddress(null);
        fail("InvalidIPAddressException not thrown.");
    } catch(InvalidIPAddressException e) {
        assertNotNull(e.getMessage());
    }
}

Ce n'est pas très différent de ce que vous avez écrit mais:

  1. Votre assertTrue(addr.getOctets() == null); est inutile.
  2. L'intention et la syntaxe sont plus claires OMI et donc plus faciles à lire.

Pourtant, c'est un peu moche. Mais c'est là que JUnit 4 vient à la rescousse, car le test d'exception est l'une des plus grandes améliorations de JUnit 4. Avec JUnit 4, vous pouvez maintenant écrire votre test comme ceci:

@Test (expected=InvalidIPAddressException.class) 
public void testNullParameter() throws InvalidIPAddressException {
    IPAddress addr = new IPAddress(null);
}

Sympa, non?

Maintenant, en ce qui concerne la vraie question, si je ne m'attends pas à ce qu'une exception soit levée, j'irais certainement pour la voie n ° 1 (car elle est moins verbeuse) et laisserais JUnit gérer l'exception et échouer au test comme prévu.

91
Pascal Thivent

Pour les tests où je ne m'attends pas à une exception, je ne prends pas la peine de l'attraper. Je laisse JUnit intercepter l'exception (il le fait de manière fiable) et ne la traite pas du tout au-delà de la déclaration de la cause throws (si nécessaire).

Je note re. votre premier exemple que vous n'utilisez pas le @expected à savoir l'annotation.

@Test (expected=IndexOutOfBoundsException.class) public void elementAt() {
    int[] intArray = new int[10];

    int i = intArray[20]; // Should throw IndexOutOfBoundsException
  }

Je l'utilise pour tous les tests que je teste pour lever des exceptions. C'est plus bref que le modèle de capture/échec équivalent que j'ai dû utiliser avec Junit3.

10
Brian Agnew

Depuis JUnit 4.7, vous avez la possibilité d'utiliser une règle ExpectedException et vous devez l'utiliser. La règle vous donne la possibilité de définir exactement la méthode appelée où l'exception doit être levée dans votre code de test. De plus, vous pouvez facilement faire correspondre une chaîne au message d'erreur de l'exception. Dans votre cas, le code ressemble à ceci:

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

    @Test
    public void test() {
        //working code here...
        expectedException.expect(InvalidIPAddressException.class);
        IPAddress addr = new IPAddress(null);
    }

MISE À JOUR: Dans son livre Tests unitaires pratiques avec JUnit et Mockito Tomek Kaczanowski s'oppose à l'utilisation d'ExpectedException, car la règle " rompt le flux d'arrangements/actes/assertions "[...] d'un test unitaire (il suggère d'utiliser Catch Exception Library à la place). Bien que je puisse comprendre son argument, je pense que l'utilisation de la règle est très bien si vous ne voulez pas introduire une autre bibliothèque tierce (il est préférable d'utiliser la règle que de saisir l'exception "manuellement" de toute façon).

9
s106mo

Pour le test nul, vous pouvez simplement faire ceci:

public void testNullParameter() {
    try {
            IPAddress addr = new IPAddress(null);
            fail("InvalidIPAddressException not thrown.");
    }
    catch(InvalidIPAddressException e) { }
}

Si l'exception a un message, vous pouvez également vérifier ce message dans la capture si vous le souhaitez. Par exemple.

String actual = e.getMessage();
assertEquals("Null is not a valid IP Address", actual);

Pour le test valide, vous n'avez pas besoin d'attraper l'exception. Un test échouera automatiquement si une exception est levée et non interceptée. Ainsi, le moyen n ° 1 serait tout ce dont vous avez besoin car il échouera et la trace de la pile sera à votre disposition de toute façon pour votre plaisir de visionnement.

0
digiarnie

Reg: test des exceptions
Je suis d'accord avec "Pascal Thivent", c'est-à-dire utiliser @Test (expected=InvalidIPAddressException.class)


Reg: test pour testValidIPAddress

IPAddress addr = new IPAddress("127.0.0.1");
byte[] octets = addr.getOctets();

J'écrirais un test comme

class IPAddressTests
{

    [Test]
    public void getOctets_ForAValidIPAddress_ShouldReturnCorrectOctect()
    {
         // Test code here
    }

}

Le point est lorsque testinput est VALID ipAddress
Le test doit porter sur les méthodes/capacités publiques de la classe, affirmant qu'elles fonctionnent comme exceptées.

0
Venu b

si je comprends votre question, la réponse est soit - préférence personnelle.

personnellement, je jette mes exceptions dans les tests. à mon avis, un test qui échoue par assertion équivaut à un test qui échoue par une exception non interceptée. les deux montrent quelque chose qui doit être corrigé.

la chose importante à retenir lors des tests est la couverture du code.

0
pstanton

De manière générale, le chemin n ° 1 est la voie à suivre, il n'y a aucune raison d'appeler un échec sur une erreur - de toute façon le test a essentiellement échoué.

Le seul moyen de temps n ° 2 est logique si vous avez besoin d'un bon message de ce qui s'est mal passé, et qu'une seule exception ne vous le donnera pas. Ensuite, la capture et l'échec peuvent être utiles pour mieux annoncer la raison de l'échec.

0
Yishai