Junit4: Présentation de la hiérarchie BiMatcher

Créé le 8 févr. 2018  ·  9Commentaires  ·  Source: junit-team/junit4

Une bonne fonctionnalité serait d'introduire la hiérarchie BiMatcher (analogue de org.hamcrest.Matcher , mais consommant 2 arguments). Alternativement, la norme Comparator peut être utilisée à cette fin. Vous pouvez également utiliser BiPredicate (cela liera JUnit à Java-8, mais ajoutera une logique AND/OR/NOT prête à l'emploi).
Quelques notes pour tout autre code :

  • BiMatcher noms BiPredicate et Comparator seront utilisés de manière interchangeable.
  • Tout le code n'est pas définitif et n'est qu'une idée générale.

Principales méthodes à ajouter dans la classe Assert :

  • assertThat(T expected, T actual, Comparator<T> biMatcher);
  • assertThat(String message, T expected, T actual, Comparator<T> biMatcher); (peut-être pas nécessaire, voir ci-dessous).
  • Plusieurs méthodes assertArray() (voir ci-dessous).

La motivation : vous devez parfois effectuer de nombreuses vérifications similaires sur des lots d'objets. Les fonctionnalités existantes le permettent, mais avec beaucoup de code supplémentaire (boucles et conditions). Avec Comparator cela peut être fait avec moins de code et avec une plus grande flexibilité.

Exemple réel (à partir duquel j'ai commencé à réfléchir à cette fonctionnalité) : besoin de tester que tous les objets du tableau sont identiques ou différents (selon un indicateur booléen).

Avec les capacités actuelles de JUnit, cela ressemblera à :
plus court:

for (int i = 0; i < elements.length - 1; i++) {
  if (checkSame) { // Same
    assertSame("elements must be the same.", elements[i], elements[i + 1]);
  } else { // Not same
    assertNotSame("elements must be not the same.", elements[i], elements[i + 1]);
  }
}

de meilleures performances (mais qui se soucie des performances dans les tests):

if (checkSame) { // Same
  for (int i = 0; i < elements.length - 1; i++) {
    assertSame("elements must be the same.", elements[i], elements[i + 1]);
  }
else { // Not same
  for (int i = 0; i < elements.length - 1; i++) {
    assertNotSame("elements must be not the same.", elements[i], elements[i + 1]);
  }
}

Nous pouvons également implémenter notre propre Comparator et utiliser assertTrue()/assertFalse() , mais le code pour créer des comparateurs sera également volumineux (sauf si nous utilisons des lambdas).

Avec la nouvelle approche, le code semblera beaucoup plus court et plus propre quant à moi, comme ceci :

Comparator<T> comparator = checkSame ? BiMatchers.same() : BiMatchers.notSame();
String message = checkSame ? "elements must be the same." : "elements must be not the same.";
for (int i = 0; i < elements.length - 1; i++) {
  assertThat(message, elements[i], elements[i + 1], comparator);
}

Pour le rendre encore plus court, nous pouvons étendre Comparator à MessagedComparator avec la propriété optionnelle message , donc JUnit le prendra à partir de là d'une manière ou d'une autre. Comme ça:

MessagedComparator<T> comparator = checkSame ? BiMatchers.same("elements must be the same.") : BiMatchers.notSame("elements must be not the same.");
for (int i = 0; i < elements.length - 1; i++) {
  assertThat(message, elements[i], elements[i + 1], comparator);
}

Les conséquences sont très importantes :
1) La classe Assert sera très flexible en général, car vous pouvez utiliser n'importe quel comparateur standard ou propre sans trop de code.
2) En raison de ce qui précède, les lambdas peuvent être utilisés plus facilement pour injecter toutes les conditions.
3) Une cascade logique supplémentaire (ET, OU, NON) peut être ajoutée prête à l'emploi (quelque chose de similaire à org.hamcrest.CoreMatchers ). Actuellement, pour toute nouvelle mini-fonctionnalité, une nouvelle méthode est requise, par exemple assertNotSame() côté de assertSame() etc. Ou encore nous avons besoin assertTrue()/assertFalse() méthodes
4) L'ensemble Assert classe

public static void assertSame(String message, Object expected, Object actual) {
  assertThat(message, expected, actual, BiMatchers.same())
}

public static void assertThat(String message, T expected, T actual, Comparator<T> biMatcher) {
...
}

ou comme déjà mentionné :

public static void assertSame(String message, Object expected, Object actual) {
  assertThat(expected, actual, BiMatchers.same(message))
}

public static void assertThat(T expected, T actual, MessagedComparator<T> biMatcher) {
...
}

5) Des méthodes assertArray() peuvent être ajoutées (donc les anciennes méthodes refactorisées). Par exemple:

public static void assertArrayEquals(boolean[] expecteds, boolean[] actuals) {
  assertArray(expecteds, actuals, BiMatchers.equals());
}

public static void assertArray(boolean[] expecteds, boolean[] actuals, Comparator<Boolean>) {
...
}

Ainsi, par exemple, vous pouvez vérifier que tous les éléments de 2 tableaux sont les mêmes que :

Assert.assertArray(array1, array2, BiMatchers.same());

La longueur du tableau ou d'autres vérifications peuvent également être contrôlées séparément. Pseudocode :

Assert.assertArray(array1, array2, and(sameLength(), same()));

Tous les 9 commentaires

Merci d'avoir soulevé ce problème.

Je préférerais que JUnit 4.x n'ajoute pas plus de DSL d'assertion. Il est vraiment difficile d'obtenir un DSL correct et flexible, et je ne suis pas sûr qu'il soit logique de consacrer du temps et des efforts à cela dans JUnit lorsqu'il existe de bons frameworks d'assertion tiers comme Truth ou Fest.

De plus, les fonctionnalités nécessitant Java 8 doivent aller sur http://github.com/junit-team/junit5

@gitIvanB Merci d'avoir ouvert le problème. Comme @kcooney , je pense également que cela serait mieux résolu par une bibliothèque d'assertions, par exemple AssertJ.

@kcooney , @marcphilipp , merci pour l'allusion. On dirait que l'utilisation des bibliothèques d'assertions est une bonne pratique ici.
Une question supplémentaire. Puis-je utiliser comme règle empirique que l'utilisation de Assert est déconseillée (ou déconseillée) ? Surtout en gardant à l'esprit la future mise à niveau possible de JUnit-4 vers JUnit-5.

@gitIvanB La question n'est pas claire. Si vous développez contre junit4, vous utilisez org.junit.Assert , si vous développez contre junit5 alors org.junit.jupiter.api.Assertions

L'approche recommandée consiste à importer statiquement des méthodes à partir de celui-ci, pour éviter d'avoir Assert. sur chaque ligne.

@gitIvanB Vous ne devriez certainement pas utiliser junit.framework.Assert , mais org.junit.Assert est très bien. Lorsque vous effectuez une mise à niveau, vous pouvez utiliser un IDE pour convertir JUnit 4 en assertions JUnit Jupiter (IDEA le prend déjà en charge). La plus grande différence est que le paramètre de message facultatif vient en premier dans JUnit 4 mais en dernier dans Jupiter. Alternativement, vous pouvez décider d'utiliser une bibliothèque d'assertions différente maintenant et continuer à l'utiliser lors de la migration de Vintage vers Jupiter.

@panchenko , @marcphilipp Donc, si j'ai bien compris, le org.junit.Assert JUnit-4 est scellé et ne sera pas enrichi de nouvelles méthodes. Dans JUnit-5, je dois utiliser soit org.junit.jupiter.api.Assertions (en remplacement de org.junit.Assert ) soit une lib d'assertion.
J'ai besoin de temps pour apprendre JUnit-5, mais à première vue dans org.junit.jupiter.api.Assertions son approche en général est similaire à org.junit.Assert . C'est donc une grande chance que je doive créer un ticket similaire pour JUnit-5 :) Je pense que JUnit-5 devrait soit fournir des assertions à part entière, soit déléguer entièrement aux bibliothèques d'assertions.

La philosophie de @gitIvanB JUnit est de fournir un bon framework extensible pour écrire des tests pour Java en Java. Étant un framework extensible, nous voyons souvent d'autres projets open source étendre le framework et/ou fournir des bibliothèques de test riches qui peuvent être utilisées avec JUnit.

Pour les assertions, il existe plusieurs projets qui fournissent un ensemble plus riche d'assertions, parfois de manière extensible.
Par exemple, il y a Google Truth (https://github.com/google/truth), FEST (https://github.com/alexruiz/fest-assert-2.x), AssertJ (http://joel- costigliola.github.io/assertj/) et Hamcrest (http://hamcrest.org/JavaHamcrest/).

Parce qu'il existe tellement de bibliothèques d'assertions incroyables, j'ai hésité à accepter de nouvelles demandes de fonctionnalités pour org.junit.Assert . Je ne le considérerais pas congelé (bien que je considérerais junit.framework.Assert congelé). En fait, 4.13 introduira assertThrows . Mais comme nous n'aurons jamais un ensemble d'affirmations aussi riche que ces projets, nous nous sentons à l'aise de fournir les bases.

En d'autres termes, des communautés saines se sont formées autour de l'utilisation et de l'entretien de ces projets, donc je pense que nous devrions les adopter et les soutenir.

Je ne peux pas parler pour JUnit5, mais j'imagine que les développeurs d'origine pensaient qu'ils ne pouvaient pas publier la prochaine génération de JUnit sans une bonne base de méthodes d'assertion (ou peut-être qu'ils pensaient être le meilleur moyen de s'assurer que le nouveau framework était fonctionnel et poli était de tester JUnit5 en utilisant JUnit5). De nouvelles pratiques de programmation les ont amenés à prendre des décisions différentes sur ce à quoi ressemblerait une API d'assertion de base que Kent, Erich et David, vous verrez donc quelques différences. Mais vous ne verrez probablement jamais dans JUnit un ensemble d'assertions aussi riche qu'avec Hamcrest ou un DSL extensible comme Truth.

Je laisse à l'équipe JUnit5 le soin de décider quels types de changements ils envisageront. Si vous sentez fortement que le
les méthodes que vous proposez sont importantes car elles constituent la base de la rédaction de bons tests, n'hésitez pas à y créer un problème.

Lorsque nous avons commencé à travailler sur JUnit 5 (nom de code "JUnit Lambda" à l'époque), nous avons discuté de la portée de ce que nous voulions réaliser. L'écriture d'un nouveau type de bibliothèque d'assertions a été explicitement décidée comme ne faisant pas partie de la portée.

Ou, comme le dit le guide de l'utilisateur JUnit 5 :

Même si les fonctionnalités d'assertion fournies par JUnit Jupiter sont suffisantes pour de nombreux scénarios de test, il y a des moments où plus de puissance et des fonctionnalités supplémentaires telles que les matchers sont souhaitées ou requises. Dans de tels cas, l'équipe JUnit recommande l'utilisation de bibliothèques d'assertions tierces telles que AssertJ, Hamcrest, Truth, etc. Les développeurs sont donc libres d'utiliser la bibliothèque d'assertions de leur choix.

Donc, ce n'est pas non plus figé, mais l'objectif est similaire à celui de JUnit 4, à savoir fournir un ensemble d'assertions de base pour permettre aux utilisateurs de démarrer. Nous utilisons même AssertJ pour nos propres tests unitaires.

Cette page vous a été utile?
0 / 5 - 0 notes