2016-03-06 35 views
9

Ho impostato alcuni test JUnit (4.12) con la funzione ExpectedException e vorrei che il test continuasse dopo l'eccezione prevista. Ma non vedo mai il log '3', poiché l'esecuzione sembra fermarsi dopo l'eccezione, l'evento se si cattura?Come continuare il test dopo JUnit ExpectedException se lanciato?

Questo è effettivamente possibile e come?

@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

Si potrebbe voler riconsiderare la scrittura di un test in cui si desidera eseguire qualcosa dopo che un'eccezione è stata lanciata. Se c'è qualcos'altro che vuoi testare, dovresti dividerli in due test diversi, uno che controlla l'eccezione e il secondo che controlla l'altra logica. –

+1

Possibile duplicato di [JUnit continua ad affermare le cose dopo l'eccezione prevista] (http://stackoverflow.com/questions/21506079/junit-continue-to-assert-things-after-expected-exception) – Joe

risposta

5

Non si può fare che, quando viene generata l'eccezione è gettato per davvero, ExpectedException regola oppure no.

Se si vuole veramente questo tipo di comportamento, si può tornare al modello di "vecchia scuola":

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

log.info("3"); 

Ma non sarebbe stato un problema per un po 'di log.

+0

OK, si conferma cosa stava pensando, nessun modo possibile con questo. Forse il mio approccio non è il massimo, come descrive @thepacker. – Deathtiny

2

Questo SO soluzione sembra di fare ciò che si vuole fare: JUnit continue to assert things after expected exception

Io stesso pensavo qualcosa di simile. Per continuare con il test, dovresti prendere l'eccezione tu stesso nel test. Questa soluzione mostra un modo elegante per farlo.

Nota: se si stabilisce una regola per prevedere un'eccezione (come hai fatto), il test ritorna correttamente non appena viene generata tale eccezione. Riferimento: http://junit.org/javadoc/latest/org/junit/rules/ExpectedException.html

1

Prima di tutto il test non verifica una cosa. Verifica "userExists" e "createUser" in condizioni diverse a.k.a. diversi scenari. Questo è chiamato AssertionRoulette. Non avresti bisogno di un hack per continuare a registrare "3", se vuoi scrivere test, che falliscono nel giusto motivo.

Se i test non riescono per il giusto motivo, è possibile vedere lo scenario perché non funziona senza eseguire tutte le operazioni di registrazione. Il Junit-Runner fa già il logging per te.

@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

Poiché userService utilizza un MongoDB dietro, il problema con tale approccio è che un metodo di test inizializzerà già i dati (createUser) e l'altro test avrà esito negativo. O devo cancellare il DB dopo ogni metodo di prova? – Deathtiny

+0

È possibile creare una regola, che crea un nonce per ciascun test e crea una piccola funzione di servizio, che creerà un nome univoco. Invece di "toto1" diventerà "YzFAE4qd_toto1".Avrai semplicemente utenti diversi per ogni test. Ho visto questo approccio non molto tempo fa - ma non ricordo dove. Aggiungerò questo alla mia risposta, se trovo di nuovo il riferimento. – thepacker

+0

È possibile cancellare il db dopo che la classe è pronta. @ BeforeClass e @ AfterClass se ne hai bisogno, ma una Regola potrebbe pulire anche gli utenti. Ci sono troppe possibilità. – thepacker

1

Se non si desidera aggiungere un sacco di metodi di prova simili per qualcosa che ha molte opzioni per generare l'eccezione prevista e desideri verificare che in realtà getta sulla tutte dei casi desiderati all'interno di un singola unità-test, invece, io suggerirei questo (non abbastanza forse) utili schema:

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

quello potrebbe anche iterare oggetti (e anche eseguire funzioni) di qualche lista preliminare preparato invece di switch-case con hard interi codificati.