Ignorer conditionnellement les tests dans JUnit 4

365

OK, donc l' @Ignoreannotation est bonne pour marquer qu'un scénario de test ne doit pas être exécuté.

Cependant, je souhaite parfois ignorer un test basé sur des informations d'exécution. Un exemple pourrait être si j'ai un test de concurrence qui doit être exécuté sur une machine avec un certain nombre de cœurs. Si ce test était exécuté sur une machine à processeur unique, je ne pense pas qu'il serait correct de simplement passer le test (car il n'a pas été exécuté), et il ne serait certainement pas correct d'échouer le test et de casser la construction .

Je veux donc pouvoir ignorer les tests au moment de l'exécution, car cela semble être le bon résultat (puisque le cadre de test permettra à la génération de passer mais d'enregistrer que les tests n'ont pas été exécutés). Je suis assez sûr que l'annotation ne me donnera pas cette flexibilité, et je pense que je devrai créer manuellement la suite de tests pour la classe en question. Cependant, la documentation ne mentionne rien à ce sujet et en regardant à travers l' API, il n'est pas clair non plus comment cela serait fait par programme (c'est-à-dire comment créer par programmation une instance de Testou similaire qui est équivalente à celle créée par l' @Ignoreannotation?).

Si quelqu'un a fait quelque chose de similaire dans le passé ou a une idée brillante de la façon dont je pourrais procéder, je serais heureux d'en entendre parler.

Andrzej Doyle
la source

Réponses:

476

La manière JUnit est de le faire au moment de l'exécution org.junit.Assume.

 @Before
 public void beforeMethod() {
     org.junit.Assume.assumeTrue(someCondition());
     // rest of setup.
 }

Vous pouvez le faire dans une @Beforeméthode ou dans le test lui-même, mais pas dans une @Afterméthode. Si vous le faites dans le test lui-même, votre @Beforeméthode sera exécutée. Vous pouvez également le faire à l'intérieur @BeforeClasspour empêcher l'initialisation de la classe.

Un échec d'hypothèse entraîne l'ignorance du test.

Edit: Pour comparer avec l' @RunIfannotation de junit-ext , leur exemple de code ressemblerait à ceci:

@Test
public void calculateTotalSalary() {
    assumeThat(Database.connect(), is(notNull()));
    //test code below.
}

Sans oublier qu'il est beaucoup plus facile de capturer et d'utiliser la connexion de la Database.connect()méthode de cette façon.

Yishai
la source
1
@notnoop, ce n'est pas du tout mon observation. Ils sont ignorés. Le programme de test IDEA les signale de cette façon et un examen du code source JUnit montre qu'il signale le test comme ignoré.
Yishai
1
Pour citer: "Dans le futur, cela pourrait changer, et une supposition échouée pourrait conduire à ignorer le test." Il a en fait changé, à partir de 4,5 je crois. Le javadoc actuel dit: "Le runner JUnit par défaut traite les tests avec des hypothèses qui échouent comme ignorés. Les runners personnalisés peuvent se comporter différemment." github.com/KentBeck/junit/blob/…
Yishai
4
Eclipse 3.6 avec Junit 4.8.1 rapporte de fausses hypothèses comme test de réussite. Idem avec la fourmi 1.8.1.
fijiaaron
8
Le fait qu'Eclipse signale des hypothèses qui ont échoué lors du passage est un bug: bugs.eclipse.org/bugs/show_bug.cgi?id=359944
Martin
1
@JeffStorey, alors vous cherchez deux ou trois choses. L'une est l' @BeforeClassannotation, où vous pouvez faire échouer votre hypothèse, ce qui ignorera toute la classe. Un autre est @ClassRule(pour le contrôle à grain fin, mais sur toute la classe, une fois).
Yishai
51

Vous devez vérifier le Junit-extprojet. Ils ont une RunIfannotation qui effectue des tests conditionnels, comme:

@Test
@RunIf(DatabaseIsConnected.class)
public void calculateTotalSalary() {
    //your code there
}

class DatabaseIsConnected implements Checker {
   public boolean satisify() {
        return Database.connect() != null;
   }
}

[Exemple de code extrait de leur tutoriel]

notnoop
la source
3
Merci pour cette réponse - une syntaxe alternative intéressante pour la fonctionnalité, bien que j'y vais Assumedirectement afin de ne pas introduire une autre dépendance.
Andrzej Doyle
3
Personnellement, je préfère cette solution. Si vous avez de nombreux tests à exécuter dans les mêmes conditions, ce serait bien plus idéal que d'avoir à utiliser Assume dans chaque test. De plus, si cela peut être utilisé au niveau de la classe plutôt qu'au niveau de la méthode, ce sera encore plus idéal.
Richard
Je le préférerais », car cela aide à exécuter le test de manière conditionnelle au moment de l'exécution. Il convient où un certain nombre de tests unitaires vont s'exécuter et l'exigence est d'exécuter les tests unitaires sur vérificateur particulier. Je suis vraiment étonné de voir que junit-ext n'est pas disponible sur le référentiel maven. Comment pourrions-nous nous prévaloir de cela dans le projet maven.
shambhu
4
Une annotation comme @RunIfsépare la condition quand un test doit être exécuté à partir du code de test réel, ce qui, je pense, est bon. Ce que je n'aime pas, c'est que cela nécessite un testeur particulier. J'ai donc écrit une règle JUnit pour ignorer conditinoally les tests.
Rüdiger Herrmann
2
Après avoir installé le pot junit-ext (trouvé ici code.google.com/p/junit-ext/downloads/… ) dans notre référentiel local et implémenté cette annotation @RunIf ... rien! Il est totalement ignoré, et je pense que la raison pourrait être que junit-ext semble dépendre de junit 4.5. Nous avons besoin de 4,9+ en raison du test de printemps. Alors ... tant pis.
Marc
7

Dans JUnit 4, une autre option pour vous peut être de créer une annotation pour indiquer que le test doit répondre à vos critères personnalisés, puis étendre le programme d'exécution par défaut avec vos propres et en utilisant la réflexion, basez votre décision sur les critères personnalisés. Cela peut ressembler à ceci:

public class CustomRunner extends BlockJUnit4ClassRunner {
    public CTRunner(Class<?> klass) throws initializationError {
        super(klass);
    }

    @Override
    protected boolean isIgnored(FrameworkMethod child) {
        if(shouldIgnore()) {
            return true;
        }
        return super.isIgnored(child);
    }

    private boolean shouldIgnore(class) {
        /* some custom criteria */
    }
}
Kyle Shrader
la source
Bien que cela semble agréable et propre, cela ne fonctionne pas avec les versions actuelles si JUnit4, car le BlockJUnit4ClassRunnerne propose plus la isIgnoredméthode.
Dave
-2

Une note rapide: Assume.assumeTrue(condition)ignore le reste des étapes mais réussit le test. Pour échouer au test, utilisez l' org.junit.Assert.fail()intérieur de l'instruction conditionnelle. Fonctionne de la même manière Assume.assumeTrue()mais échoue au test.

TIn TIn
la source
5
Comme indiqué dans les réponses ci-dessus, une hypothèse échouée ne fait pas passer le test, elle renvoie un état distinct. Certains coureurs peuvent signaler par erreur cela comme s'il s'agissait d'une réussite, mais c'est une faiblesse / bogue dans le coureur de test (et le coureur JUnit par défaut affiche le test comme ignoré). Et quant à votre dernière phrase, l'échec du test n'est précisément pas ce que je veux (ed) faire.
Andrzej Doyle
Ah d'accord. Les tests ont réussi l'hypothèse de l'échec dans mon cas, mais je voulais qu'ils soient signalés comme ayant échoué (je cherchais une exception de Test Watcher). Forcer un échec m'a aidé.
TIn TIn