2010-02-01 16 views
66

JUnit 4.8 contiene una nuova funzionalità chiamata "Categorie" che consente di raggruppare determinati tipi di test insieme. Questo è molto utile, ad es. avere prove di test separate per test lenti e veloci. Conosco le cose menzionate in JUnit 4.8 release notes, ma vorrei sapere come posso effettivamente eseguire tutti i test annotati con determinate categorie.Come eseguire tutti i test appartenenti a una determinata categoria in JUnit 4

Le JUnit 4.8 note di rilascio mostrano un esempio di definizione suite, dove SuiteClasses annotazione seleziona i test da certa categoria di correre, in questo modo:

@RunWith(Categories.class) 
@IncludeCategory(SlowTests.class) 
@SuiteClasses({ A.class, B.class }) // Note that Categories is a kind of Suite 
public class SlowTestSuite { 
    // Will run A.b and B.c, but not A.a 
} 

Qualcuno sa come avrei potuto eseguire tutte le prove in categoria SlowTests ? Sembra che tu debba avere l'annotazione SuiteClasses ...

+1

Ciao. Ho una domanda correlata. sentiti libero di carillon in: http://stackoverflow.com/questions/15776718/using-junit-categories-vs-simply-organizing-logical-test-categories-in-separate – amphibient

risposta

59

Ho scoperto un modo per ottenere ciò che voglio, ma non ritengo che questa sia la migliore soluzione possibile poiché si basa sulla libreria ClassPathSuite che non fa parte di JUnit.

ho definire la suite di test per le prove lente come questo:

@RunWith(Categories.class) 
@Categories.IncludeCategory(SlowTests.class) 
@Suite.SuiteClasses({ AllTests.class }) 
public class SlowTestSuite { 
} 

alltests classe è definita in questo modo:

@RunWith(ClasspathSuite.class) 
public class AllTests { 
} 

ho dovuto utilizzare la classe ClassPathSuite da ClassPathSuite progetto qui. Troverà tutte le classi con i test.

+4

In realtà è una soluzione ragionevole. Grazie per aver soddisfatto la tua domanda poiché è davvero buona :-) –

+0

Per chiunque si chieda come automatizzare l'esecuzione di una categoria di test (con questa esatta configurazione) usando Ant, [questa domanda] (http://stackoverflow.com/questions/6226026/how-to-run-all-j-unit-tests-in-a-category-suite-con-ant) potrebbe essere utile. – Jonik

+3

Come complimento alla mia domanda http://stackoverflow.com/q/2698174/59470 e spiegazione dettagliata ho aggiunto un post di blog: http://novyden.blogspot.com/2011/06/using-junit-4-categories -Rimpiazzare.html – topchef

1

Non sono sicuro, qual è esattamente il tuo problema.

Basta aggiungere tutti i test a una suite (o al baratro di suite). Quindi utilizzare l'annotazione Categorie e Includi/Escludi categorie per specificare le categorie che si desidera eseguire.

Una buona idea potrebbe essere quella di avere una suite contenente tutti i test e un paio di suite separate che fanno riferimento al primo, specificando il diverso insieme di categorie che avete richiesto.

+19

Il mio problema è che ho migliaia di test e Non voglio aggiungerli manualmente a nessuna suite. Voglio solo che vengano eseguiti i test con determinate categorie. Non dovrebbe essere così difficile per JUnit scoprire quali test hanno certe annotazioni come lo sono effettivamente quando si trovano i metodi di test. – Kaitsu

1

Non è una risposta diretta al problema, ma forse l'approccio generale potrebbe essere migliorata ...

Perché sono i vostri test lento? Forse il set-up dura a lungo (database, I/O, ecc.), Forse i test stanno testando troppo? In tal caso, separerei i veri test unitari da quelli "di lunga durata", che spesso sono test di integrazione.

Nelle mie installazioni ho env di staging, dove i test di unità vengono eseguiti spesso e test di integrazione costantemente ma più raramente (ad es. Dopo ogni commit nel controllo di versione). Non ho mai lavorato con il raggruppamento per i test unitari, perché dovrebbero essere tutti accoppiati liberamente. Lavoro solo con il raggruppamento e la relazione dei casi di test nelle impostazioni di test di integrazione (ma con TestNG).

Ma è bello sapere che JUnit 4.8 ha introdotto alcune funzionalità di raggruppamento.

+1

Grazie Manuel per i tuoi commenti! Non ho davvero bisogno di separare i test unitari, ma uso JUnit anche per i test di integrazione e voglio separarli dai test unitari. Ho anche guardato TestNG e sembra che i test (e non solo i test delle unità) siano più belli di quelli di JUnit. E ha anche una documentazione migliore e un buon libro. – Kaitsu

7

Ecco alcune delle principali differenze tra TestNG e JUnit quando si tratta di gruppi (o categorie, come JUnit li chiama):

  • JUnit di vengono digitati (annotazioni), mentre TestNG di sono stringhe. Ho fatto questa scelta perché volevo essere in grado di usare espressioni regolari durante l'esecuzione di test, ad esempio "esegui tutti i test che appartengono al gruppo" database * ".Inoltre, dover creare una nuova annotazione ogni volta che è necessario creare una nuova categoria è fastidioso, anche se ha il vantaggio che un IDE ti dirà immediatamente dove questa categoria viene utilizzata (TestNG ti mostra questo nei suoi report).

  • TestNG separa in modo molto chiaro il modello statico (il codice dei test) dal modello di runtime (i test vengono eseguiti). Se vuoi prima eseguire i gruppi "front-end" e poi "servlet", puoi farlo senza dover ricompilare nulla. Poiché JUnit definisce i gruppi nelle annotazioni e devi specificare queste categorie come parametri per il corridore, in genere devi ricompilare il codice ogni volta che desideri eseguire un diverso insieme di categorie, il che, a mio parere, contrasta lo scopo.

+0

Abbiamo creato il nostro supporto per categorie nei nostri test JUnit in modo molto simile a JUnit, la differenza principale è che al posto dell'annotazione @ Categories.IncludeCategory, abbiamo reso il nostro configurabile tramite una proprietà di sistema. Perché questo è stato troppo difficile per JUnit fare per noi è la supposizione di chiunque. – Trejkaz

2

Per eseguire i test categorizzati senza specificare tutti loro explicily in @Suite.SuiteClasses annotazione è possibile fornire la propria implementazione della Suite. Ad esempio un org.junit.runners.ParentRunner può essere esteso. Invece di utilizzare una serie di classi fornite da @Suite.SuiteClasses, la nuova implementazione deve eseguire la ricerca di test categorizzati in classpath.

Vedere this project come un esempio di tale approccio. Uso:

@Categories(categoryClasses = {IntegrationTest.class, SlowTest.class}) 
@BasePackage(name = "some.package") 
@RunWith(CategorizedSuite.class) 
public class CategorizedSuiteWithSpecifiedPackage { 

} 
5

Uno svantaggio per la soluzione di Kaitsu è che Eclipse verrà eseguito i test due volte, e le SlowTests 3 volte, durante l'esecuzione di tutte le prove in un progetto. Questo perché Eclipse eseguirà tutti i test, quindi la suite AllTests, quindi la SlowTestSuite.

Ecco una soluzione che prevede la creazione di sottoclassi dei runner di test della soluzione Kaitsu per saltare le suite a meno che non sia impostata una certa proprietà di sistema. Un trucco vergognoso, ma tutto quello che ho inventato finora.

@RunWith(DevFilterClasspathSuite.class) 
public class AllTests {} 

.

@RunWith(DevFilterCategories.class) 
@ExcludeCategory(SlowTest.class) 
@SuiteClasses(AllTests.class) 
public class FastTestSuite 
{ 
} 

.

public class DevFilterCategories extends Suite 
{ 
    private static final Logger logger = Logger 
     .getLogger(DevFilterCategories.class.getName()); 
    public DevFilterCategories(Class<?> suiteClass, RunnerBuilder builder) throws InitializationError { 
     super(suiteClass, builder); 
     try { 
      filter(new CategoryFilter(getIncludedCategory(suiteClass), 
        getExcludedCategory(suiteClass))); 
      filter(new DevFilter()); 
     } catch (NoTestsRemainException e) { 
      logger.info("skipped all tests"); 
     } 
     assertNoCategorizedDescendentsOfUncategorizeableParents(getDescription()); 
    } 

    private Class<?> getIncludedCategory(Class<?> klass) { 
     IncludeCategory annotation= klass.getAnnotation(IncludeCategory.class); 
     return annotation == null ? null : annotation.value(); 
    } 

    private Class<?> getExcludedCategory(Class<?> klass) { 
     ExcludeCategory annotation= klass.getAnnotation(ExcludeCategory.class); 
     return annotation == null ? null : annotation.value(); 
    } 

    private void assertNoCategorizedDescendentsOfUncategorizeableParents(Description description) throws InitializationError { 
     if (!canHaveCategorizedChildren(description)) 
      assertNoDescendantsHaveCategoryAnnotations(description); 
     for (Description each : description.getChildren()) 
      assertNoCategorizedDescendentsOfUncategorizeableParents(each); 
    } 

    private void assertNoDescendantsHaveCategoryAnnotations(Description description) throws InitializationError {   
     for (Description each : description.getChildren()) { 
      if (each.getAnnotation(Category.class) != null) 
       throw new InitializationError("Category annotations on Parameterized classes are not supported on individual methods."); 
      assertNoDescendantsHaveCategoryAnnotations(each); 
     } 
    } 

    // If children have names like [0], our current magical category code can't determine their 
    // parentage. 
    private static boolean canHaveCategorizedChildren(Description description) { 
     for (Description each : description.getChildren()) 
      if (each.getTestClass() == null) 
       return false; 
     return true; 
    } 
} 

.

public class DevFilterClasspathSuite extends ClasspathSuite 
{ 
    private static final Logger logger = Logger 
     .getLogger(DevFilterClasspathSuite.class.getName()); 
    public DevFilterClasspathSuite(Class<?> suiteClass, RunnerBuilder builder) 
     throws InitializationError { 
     super(suiteClass, builder); 
     try 
     { 
      filter(new DevFilter()); 
     } catch (NoTestsRemainException e) 
     { 
      logger.info("skipped all tests"); 
     } 
    } 
} 

.

public class DevFilter extends Filter 
{ 
    private static final String RUN_DEV_UNIT_TESTS = "run.dev.unit.tests"; 

    @Override 
    public boolean shouldRun(Description description) 
    { 
     return Boolean.getBoolean(RUN_DEV_UNIT_TESTS); 
    } 

    @Override 
    public String describe() 
    { 
     return "filter if "+RUN_DEV_UNIT_TESTS+" system property not present"; 
    } 
} 

Così, nel vostro launcher FastTestSuite, basta aggiungere -Drun.dev.unit.tests = true agli argomenti VM. (Si noti che questa soluzione fa riferimento a una suite di test veloce anziché a una versione lenta.)

Problemi correlati