71 votes

Comment exécuter tous les tests appartenant à une certaine catégorie dans JUnit 4

JUnit 4.8 contient une nouvelle fonctionnalité appelée "Catégories" qui vous permet de regrouper certains types de tests ensemble. C'est très utile, par exemple pour séparer les séries de tests de ralentir et de tests rapides. Je sais que les trucs mentionnés dans JUnit 4.8 notes de version, mais je voudrais savoir comment je peux effectivement exécuter tous les tests annoté avec une certaine catégorie.

JUnit 4.8 notes de version de montrer un exemple de la suite de la définition, où SuiteClasses annotation sélectionne les tests de certaines catégories de courir, comme ceci:

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

Quelqu'un sait-il comment je pourrais exécuter tous les tests de SlowTests catégorie? Il semble que vous devez avoir la SuiteClasses annotation...

61voto

Kaitsu Points 2303

J'ai trouvé un moyen possible d'atteindre ce que je veux, mais je ne considère pas que cela soit la meilleure solution possible, puisqu'il repose sur ClassPathSuite bibliothèque qui ne fait pas partie de JUnit.

Je définis la suite de tests pour la lenteur des tests de ce genre:

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

AllTests classe est définie comme ceci:

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

J'ai eu à utiliser ClassPathSuite classe de ClassPathSuite projet ici. Il va trouver toutes les classes avec des tests.

7voto

Cedric Beust Points 7209

Voici certaines des principales différences entre TestNG et JUnit quand il s'agit de groupes (ou catégories, comme JUnit appelle):

  • JUnit sont typés (annotations), tandis que TestNG sont des chaînes de caractères. J'ai fait ce choix parce que je voulais être en mesure d'utiliser des expressions régulières lors de l'exécution des tests, par exemple "exécuter tous les tests qui appartiennent au groupe "base de données*". Aussi, avoir à créer une nouvelle annotation lorsque vous avez besoin de créer une nouvelle catégorie est ennuyeux, mais il a l'avantage que l'IDE va vous dire tout de suite où cette catégorie est utilisée (TestNG vous montre cela dans ses rapports).

  • TestNG sépare très clairement votre modèle statique (le code de vos tests) à partir du modèle d'exécution (qui essais de se faire rouler). Si vous souhaitez exécuter les groupes de "front-end" d'abord et ensuite "servlets", vous pouvez le faire sans avoir à recompiler quoi que ce soit. Parce que JUnit définit des groupes dans les annotations et vous devez spécifier ces catégories comme paramètres pour le coureur, vous devez recompiler votre code chaque fois que vous voulez exécuter un ensemble différent de catégories, ce qui est contraire au but, à mon avis.

5voto

Kevin Wong Points 3730

Un inconvénient de Kaitsu la solution est que Eclipse exécuter vos tests deux fois, et la SlowTests 3 fois, lors de l'exécution de tous les tests dans un projet. C'est parce que l'Éclipse va exécuter tous les tests, puis la AllTests suite, puis la SlowTestSuite.

Voici une solution qui consiste à créer des sous-classes de la Kaitsu test de la solution de coureurs à ignorer les suites à moins d'un certain système de propriété est définie. Un honteux bidouillage, mais tout ce que j'ai mis au point jusqu'à présent.

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

Donc, dans votre FastTestSuite lanceur, il suffit d'ajouter -Drun.dev.de l'unité.tests=true à la VM arguments. (Notez que cette solution fait référence à un test rapide de suite au lieu d'un lent.)

1voto

Jens Schauder Points 23468

Je ne sais pas exactement quel est votre problème.

Ajoutez simplement tous les tests à une suite (ou à une hirachie de suites). Utilisez ensuite les annotations Categories Runner et Include / ExcludeCategory pour spécifier les catégories que vous souhaitez exécuter.

Une bonne idée serait peut-être d’avoir une suite contenant tous les tests et deux suites distinctes référant à la première, spécifiant les différentes catégories de catégories dont vous avez besoin.

0voto

manuel aldana Points 4317

Pas une réponse directe à votre question, mais peut-être que l'approche générale qui pourrait être amélioré...

Pourquoi vos tests lent? Peut-être que le set-up de longue durée (base de données, I/O, etc.), peut-être que les tests sont des tests de trop? Si c'est le cas, je voudrais séparer le réel de l'unité de tests à partir de la "longue course", qui, souvent, sont en effet des tests d'intégration.

Dans mes réglages j'ai mise en scène env, où l'unité-les tests sont exécutés souvent et intégration-tests constamment, mais plus rarement (par exemple, après chaque commit dans le contrôle de version). Je n'ai jamais travaillé avec de regroupement pour les tests unitaires, parce qu'ils doivent être faiblement couplés ensemble. Je ne travaille qu'avec des regroupements et de la relation de cas de test d'intégration,-les installations d'essai (mais avec TestNG).

Mais bon de savoir que JUnit 4.8 introduit quelques groupement de fonctionnalités.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X