2016-03-06 5 views
9

J'ai mis en place un test JUnit (4.12) avec la fonction ExpectedException, et je voudrais que le test continue après l'exception attendue. Mais je ne vois jamais le journal '3', car l'exécution semble s'arrêter après l'exception, événement si attraper?Comment continuer le test après JUnit ExpectedException si lancé?

Est-ce réellement possible, et comment?

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

@Test 
public void testUserAlreadyExists() throws Exception { 
    log.info("1"); 

    // Create some users 
    userService.createUser("toto1"); 
    userService.createUser("toto2"); 
    userService.createUser("toto3"); 
    Assert.assertTrue(userService.userExists("toto1")); 
    Assert.assertTrue(userService.userExists("toto2")); 
    Assert.assertTrue(userService.userExists("toto3")); 

    log.info("2"); 

    // Try to create an existing user 
    exception.expect(AlreadyExistsException.class); 
    userService.createUser("toto1"); 

    log.info("3"); 
} 
+0

Vous pourriez vouloir reconsidérer écrire un tel test où vous voulez exécuter quelque chose après qu'une exception soit levée. S'il y a autre chose que vous voulez tester, vous devez les séparer en deux tests différents, l'un vérifiant l'exception et l'autre vérifiant l'autre logique. –

+1

Possible duplicata de [JUnit continuer à affirmer des choses après exception prévue] (http://stackoverflow.com/questions/21506079/junit-continue-to-assert-things-after-expected-exception) – Joe

Répondre

5

Vous ne pouvez pas le faire, lorsque l'exception est levée, il est jeté pour la règle réelle, ExpectedException ou non.

Si vous voulez vraiment ce genre de comportement, vous pouvez revenir au modèle « old school »:

try { 
    userService.createUser("toto1"); 
    Assert.fail("expecting some AlreadyExistsException here") 
} catch (AlreadyExistsException e) { 
    // ignore 
} 

log.info("3"); 

Mais je ne voudrais pas déranger pour un journal.

+0

OK, vous confirmez ce que je pensait, pas de possibilité avec cela. Peut-être que mon approche n'est pas la meilleure, comme le décrit @thepacker. – Deathtiny

2

Cette solution semble SO faire ce que vous voulez faire: JUnit continue to assert things after expected exception

je me disais quelque chose de similaire. Pour continuer le test, vous devrez vous-même attraper l'exception dans le test. Cette solution montre une façon élégante de le faire.

Remarque: Si vous créez une règle pour attendre une exception (comme vous l'avez fait), le test renverra le test dès que cette exception est levée. Référence: http://junit.org/javadoc/latest/org/junit/rules/ExpectedException.html

1

Tout d'abord, votre test ne teste rien. Il teste "userExists" et "createUser" dans des conditions différentes a.k.a. différents scénarios. C'est ce qu'on appelle une AssertionRoulette. Vous n'auriez pas besoin d'un hack pour continuer à enregistrer "3", si vous écrivez des tests, qui échouent pour la bonne raison.

Si les tests échouent pour la bonne raison, vous pouvez voir le scénario pourquoi il échoue sans faire tous les trucs de journalisation. Le Junit-Runner fait la journalisation pour vous déjà.

@Test 
public void testUserExists_UserCreatedUserNotExistent_expectTrue() 
{ 
    // Create some users 
    userService.createUser("toto1"); 

    // Assert That user exists 
    Assert.assertTrue(userService.userExists("toto1")); 
} 

@Test 
public void testCreateUser_UserAlreadyCreated_expectAlreadyExistsExceptionIsThrown() 
{ 
    // Create some users 
    userService.createUser("toto1"); 

    // Try to create an existing user 
    exception.expect(AlreadyExistsException.class); 
    userService.createUser("toto1");  
} 
+0

Comme le userService utilise un MongoDB derrière, le problème avec cette approche est qu'une méthode de test initialise déjà les données (createUser) et que l'autre test échouera. Ou devrais-je effacer le DB après chaque méthode de test? – Deathtiny

+0

Vous pouvez créer une règle qui crée un nonce pour chaque test et créer une petite fonction de service qui créera un nom unique. Au lieu de "toto1", il deviendra "YzFAE4qd_toto1".Vous aurez simplement différents utilisateurs pour chaque test. J'ai vu cette approche il n'y a pas longtemps - mais je ne me souviens plus où. J'ajouterai cela à ma réponse, si je retrouve la référence. – thepacker

+0

Vous pouvez effacer la base de données une fois que la classe est prête. @ BeforeClass et @ AfterClass si vous en avez besoin, mais une règle pourrait également nettoyer les utilisateurs. Il y a trop de possibilités. – thepacker

1

Si vous ne voulez pas ajouter beaucoup de méthodes d'essai similaires pour quelque chose qui a beaucoup d'options à jeter l'exception attendue et que vous voulez vérifier qu'il jette en fait sur tous des cas souhaités dans un simple test d'unité à la place, je vous suggère de cela (pas assez peut-être) schéma utile:

@Test 
public void testThatSomethingExpectedlyFails() { 
    for (int i = 1; i <= 3; i++) { 
     try { 
      switch (i) { 
       case 1: // smth here throws the exception when configuration #1; 
       case 2: // smth here throws the exception when configuration #2; 
       case 3: // smth here throws the exception when configuration #3; 
      } 
     } catch (ExceptionThatIsExpected expected) { 
      continue; 
     } catch (Exception unexpected) { 
      /* the test must fail when an unexpected exception is thrown */     
      fail("The test has failed due to an unexpected exception: " + unexpected.getMessage()); // or just re-throw this exception 
     } 

     /* the test must fail when a case completes without the expected exception */ 
     fail("No expected exception occurred at case " + i); 
    } 
} 

L'on pourrait aussi les éléments itérer (et même exécuter des fonctions) de certaines liste préparée à titre préliminaire au lieu de l'interrupteur cas avec disque entiers codés.