2017-08-18 4 views
3

Actuellement, chaque fois que je dois à l'échec d'un test en réponse à une exception levée dans un autre thread, j'écris quelque chose comme ceci:A défaut d'un test unitaire si une exception est levée dans un autre thread

package com.example; 

import java.util.ArrayList; 
import java.util.List; 
import org.testng.annotations.Test; 

import static java.util.Arrays.asList; 
import static java.util.Collections.synchronizedList; 
import static org.testng.Assert.fail; 

public final class T { 
    @Test 
    public void testFailureFromLambda() throws Throwable { 
    final List<Throwable> errors = synchronizedList(new ArrayList<>()); 

    asList("0", "1", "2").parallelStream().forEach(s -> { 
     try { 
     /* 
     * The actual code under test here. 
     */ 
     throw new Exception("Error " + s); 
     } catch (final Throwable t) { 
     errors.add(t); 
     } 
    }); 

    if (!errors.isEmpty()) { 
     errors.forEach(Throwable::printStackTrace); 

     final Throwable firstError = errors.iterator().next(); 
     fail(firstError.getMessage(), firstError); 
    } 
    } 
} 

Une liste synchronisée peut être remplacé par un AtomicReference<Throwable>, mais en général le code reste à peu près le même.

Y at-il standard (et moins bavard) façon de faire la même chose en utilisant l'un des cadres de test disponibles dans Java (TestNG, JUnit, Hamcrest, AssertJ, etc.)?

Répondre

4

Par défaut, TestNG échoue à une méthode de test lorsqu'une exception est levée. Je crois que la même chose se produit avec JUnit, où il marque un test comme errored, si elle déclenche une exception inattendue.

Si vous traitez avec Streams, vous devrez alors l'inclure dans une variante RuntimeException, afin que Java ne se plaint pas. TestNG échouerait automatiquement le test.

Voici un exemple:

@Test 
public void testFailureFromLambdaRefactored() { 
    asList("0", "1", "2").parallelStream().forEach(s -> { 
     try { 
     /* 
     * The actual code under test here. 
     */ 
      if (s.equals("2")) { 
       throw new Exception("Error " + s); 
      } 
     } catch (final Throwable t) { 
      throw new RuntimeException(t); 
     } 
    }); 
} 

Ce fut pour les scénarios qui impliquent lambdas et cours d'eau. En général, si vous souhaitez connaître une exception qui se produit dans un nouveau thread dérivé d'une méthode @Test, vous devez utiliser ExecutorService.

Voici un exemple:

@Test 
public void testFailureInAnotherThread() throws InterruptedException, ExecutionException { 
    List<String> list = asList("0", "1", "2"); 
    ExecutorService service = Executors.newFixedThreadPool(2); 
    List<Future<Void>> futures = service.invokeAll(Arrays.asList(new Worker(list))); 
    for (Future future : futures) { 
     future.get(); 
    } 

} 

public static class Worker implements Callable<Void> { 
    private List<String> list; 

    public Worker(List<String> list) { 
     this.list = list; 
    } 

    @Override 
    public Void call() throws Exception { 
     for (String s : list) { 
      if (s.equals("2")) { 
       throw new Exception("Error " + s); 
      } 
     } 
     return null; 
    } 
} 
+0

si je veux éviter à défaut l'ensemble du test et continuer affirmations douces si une exception est levée dans l'une des affirmations douces? – enissay

+0

@enonce - Par défaut, SoftAssertions échoue à un test uniquement après que toutes les assertions ont été validées. –

+0

ce n'est pas mon expérience avec ça. Peut-être que je me trompe. J'ai posté cette [question] (https://stackoverflow.com/q/46681601/3619769), j'espère que vous seriez en mesure de m'aider :) – enissay