Je sais qu'une façon de procéder serait:
@Test
public void foo(){
try{
//execute code that you expect not to throw Exceptions.
}
catch(Exception e){
fail("Should not have thrown any exception");
}
}
Existe-t-il un moyen plus propre de procéder? (Vous utilisez probablement Junit @Rule
?)
java
unit-testing
junit
exception-handling
junit4
Ankit Dhingra
la source
la source
Réponses:
Vous vous en approchez mal. Testez simplement vos fonctionnalités: si une exception est levée, le test échouera automatiquement. Si aucune exception n'est levée, vos tests deviendront tous verts.
J'ai remarqué que cette question suscite de temps en temps de l'intérêt, je vais donc m'étendre un peu.
Contexte des tests unitaires
Lorsque vous effectuez des tests unitaires, il est important de définir vous-même ce que vous considérez comme une unité de travail. Fondamentalement: une extraction de votre base de code qui peut ou non inclure plusieurs méthodes ou classes qui représente une seule fonctionnalité.
Ou, comme défini dans The art of Unit Testing, 2nd Edition by Roy Osherove , page 11:
Ce qui est important à réaliser, c'est qu'une unité de travail n'est généralement pas seulement une méthode mais au niveau très basique c'est une méthode et ensuite elle est encapsulée par une autre unité de travail.
Idéalement, vous devriez avoir une méthode de test pour chaque unité de travail séparée afin que vous puissiez toujours voir immédiatement où les choses vont mal. Dans cet exemple, il existe une méthode de base appelée
getUserById()
qui retournera un utilisateur et il y a un total de 3 unités de travaux.La première unité de travail doit tester si un utilisateur valide est retourné ou non dans le cas d'une entrée valide et invalide.
Toutes les exceptions levées par la source de données doivent être traitées ici: si aucun utilisateur n'est présent, un test doit démontrer qu'une exception est levée lorsque l'utilisateur est introuvable. Un exemple de ceci pourrait être celui
IllegalArgumentException
qui est pris avec l'@Test(expected = IllegalArgumentException.class)
annotation.Une fois que vous avez géré toutes vos valises pour cette unité de travail de base, vous montez d'un niveau. Ici, vous faites exactement la même chose, mais vous ne gérez que les exceptions qui viennent du niveau juste en dessous de l'actuel. Cela maintient votre code de test bien structuré et vous permet de parcourir rapidement l'architecture pour trouver où les choses tournent mal, au lieu d'avoir à sauter partout.
Gestion d'une entrée valide et défectueuse d'un test
À ce stade, il devrait être clair comment nous allons gérer ces exceptions. Il existe 2 types d'entrée: entrée valide et entrée défectueuse (l'entrée est valide au sens strict, mais elle n'est pas correcte).
Lorsque vous travaillez avec une entrée valide, vous définissez l'espérance implicite que le test que vous écrivez fonctionnera.
Un tel appel de méthode peut ressembler à ceci:
existingUserById_ShouldReturn_UserObject
. Si cette méthode échoue (par exemple: une exception est levée), vous savez que quelque chose s'est mal passé et vous pouvez commencer à creuser.En ajoutant un autre test (
nonExistingUserById_ShouldThrow_IllegalArgumentException
) qui utilise l' entrée défectueuse et attend une exception, vous pouvez voir si votre méthode fait ce qu'elle est censée faire avec une mauvaise entrée.TL; DR
Vous essayiez de faire deux choses dans votre test: vérifier une entrée valide et défectueuse. En divisant cela en deux méthodes qui font chacune une chose, vous aurez des tests beaucoup plus clairs et une bien meilleure vue d'ensemble des problèmes.
En gardant à l'esprit l'unité de travail en couches, vous pouvez également réduire le nombre de tests dont vous avez besoin pour une couche plus élevée dans la hiérarchie, car vous n'avez pas à tenir compte de tout ce qui pourrait avoir mal tourné dans les couches inférieures: le les couches en dessous de la couche actuelle sont une garantie virtuelle que vos dépendances fonctionnent et si quelque chose ne va pas, c'est dans votre couche actuelle (en supposant que les couches inférieures ne génèrent aucune erreur elles-mêmes).
la source
expected
annotation. Si vous voulez tester un scénario où votre code échoue et que vous voulez voir si l'erreur est correctement gérée: utilisezexpected
et peut-être utilisez asserts pour déterminer si elle a été résolue.throws IllegalArgumentException
à votre test. Ce que vous voulez à la fin, c'est que votre test vire au rouge s'il y a une exception. Bien devinez quoi? Vous n'avez pas besoin d'écrirefail()
. Comme l'a écrit @Jeroen Vannevel: "si une exception est levée, le test échouera automatiquement."Je suis tombé dessus à cause de la règle de SonarQube "squid: S2699": "Ajoutez au moins une assertion à ce cas de test."
J'ai eu un test simple dont le seul but était de passer sans lancer d'exceptions.
Considérez ce code simple:
Quel type d'affirmation peut être ajouté pour tester cette méthode? Bien sûr, vous pouvez essayer de le contourner, mais ce n'est que du ballonnement de code.
La solution vient de JUnit lui-même.
Dans le cas où aucune exception n'est levée et que vous souhaitez illustrer explicitement ce comportement, ajoutez simplement
expected
comme dans l'exemple suivant:Test.None.class
est la valeur par défaut pour la valeur attendue.la source
Avec AssertJ fluent assertions 3.7.0 :
la source
JUnit 5 (Jupiter) fournit trois fonctions pour vérifier l'absence / présence d'exception:
●
assertAll()
Affirme que tous les éléments fournis
executables
ne lèvent pas d'exceptions.
●
assertDoesNotThrow()
Affirme que l'exécution du /
fourni ne génère aucune exception .
executable
supplier
Cette fonction est disponible
depuis JUnit 5.2.0 (29 avril 2018).
●
assertThrows()
Affirme que l'exécution du fourni
executable
lève une exception de
expectedType
et renvoie l' exception .
Exemple
la source
Java 8 rend cela beaucoup plus facile, et Kotlin / Scala doublement.
On peut écrire une petite classe utilitaire
puis votre code devient simplement:
Si vous n'avez pas accès à Java-8, j'utiliserais une installation java douloureusement ancienne: des blocs de code aribitrary et un simple commentaire
Et enfin, avec kotlin, une langue dont je suis récemment tombé amoureux:
Bien qu'il y ait beaucoup de place pour jouer exactement avec la façon dont vous voulez exprimer cela, j'ai toujours été un fan d' affirmations fluides .
En ce qui concerne
Ceci est correct en principe mais incorrect en conclusion.
Java autorise des exceptions pour le flux de contrôle. Ceci est fait par le runtime JRE lui-même dans les API comme
Double.parseDouble
via aNumberFormatException
etPaths.get
via aInvalidPathException
.Étant donné que vous avez écrit un composant qui valide les chaînes numériques
Double.ParseDouble
, peut-être en utilisant une expression régulière, peut-être un analyseur manuscrit, ou peut-être quelque chose qui incorpore d'autres règles de domaine qui restreignent la plage d'un double à quelque chose de spécifique, la meilleure façon de tester cela composant? Je pense qu'un test évident serait d'affirmer que, lorsque la chaîne résultante est analysée, aucune exception n'est levée. J'écrirais ce test à l'aide de ce qui précèdeassertDoesNotThrow
ou du/*comment*/{code}
bloc. Quelque chose commeJe vous encourage également à paramétrer ce test en
input
utilisantTheories
ouParameterized
pour que vous puissiez plus facilement réutiliser ce test pour d'autres entrées. Alternativement, si vous voulez devenir exotique, vous pouvez opter pour un outil de génération de test (et ce ). TestNG prend mieux en charge les tests paramétrés.Ce que je trouve particulièrement désagréable, c'est la recommandation d'utiliser
@Test(expectedException=IllegalArgumentException.class)
, cette exception est dangereusement large . Si votre code change de manière à ce que le composant sous le constructeur du test aitif(constructorArgument <= 0) throw IllegalArgumentException()
, et que votre test fournisse 0 pour cet argument parce qu'il était pratique - et c'est très courant, parce que la bonne génération de données de test est un problème étonnamment difficile -, alors votre test sera une barre verte même si elle ne teste rien. Un tel test est pire qu'inutile.la source
Assert.assertThrows
pour vérifier que du code lève une exception.Si vous n'avez pas la chance d'attraper toutes les erreurs dans votre code. Vous pouvez faire bêtement
la source
Exception ex
devrait être= null;
avant de pouvoir la tester.JUnit5 ajoute la méthode assertAll () dans ce but précis.
source: API JUnit 5
la source
Bien que ce poste ait maintenant 6 ans, beaucoup de choses ont changé dans le monde Junit. Avec Junit5, vous pouvez désormais utiliser
Ex:
J'espère que cela aidera les gens qui utilisent la nouvelle version de Junit5
la source
Awaitility
l' intérieuruntilAsserted(ThrowingRunnable assertion)
. Le système testé teste actuellement une exception spécifique sur le ThrowingRunnable que je fournis, mais je veux lui donner un peu de temps jusqu'à ce qu'il cesse de le faire. Cependant, si cela provoquait une exception différente, j'aimerais que le test échoue instantanément.Si vous voulez tester si votre cible de test consomme l'exception. Laissez simplement le test comme (simulateur de collaborateur utilisant jMock2):
Le test réussirait si votre cible consomme l'exception levée, sinon le test échouerait.
Si vous voulez tester votre logique de consommation d'exception, les choses deviennent plus complexes. Je suggère de déléguer la consommation à un collaborateur dont on pourrait se moquer. Par conséquent, le test pourrait être:
Mais parfois, il est trop conçu si vous voulez simplement le journaliser. Dans ce cas, cet article ( http://java.dzone.com/articles/monitoring-declarative-transac , http://blog.novoj.net/2008/09/20/testing-aspect-pointcuts-is-there -an-easy-way / ) peut aider si vous insistez sur tdd dans ce cas.
la source
Utilisez assertNull (...)
la source
assertNull
n'est jamais exécuté non plus. Cependant, le lecteur rapide a l'impression qu'une affirmation est faite qui vérifie vraiment le cas de non-lancement. En d'autres termes: si le bloc catch est atteint, l'exception est toujours non nulle - elle peut donc être remplacée par un simplefail
.assertNull(e)
rapportera que le test a échoué, car comme indiquée
ne peut pas êtrenull
dans lecatch
bloc ... Mike c'est juste une programmation bizarre: - /. .. oui au moins utiliserfail()
comme le dit AndreasVous pouvez vous attendre à ce que cette exception ne soit pas levée en créant une règle.
la source
Ce n'est peut-être pas le meilleur moyen, mais cela garantit définitivement que l'exception n'est pas levée du bloc de code en cours de test.
la source
Vous pouvez le faire en utilisant un @Rule puis appelez la méthode reportMissingExceptionWithMessage comme indiqué ci-dessous: Ceci est du code Scala.
la source
private val
? Quel est ce langage? Clairement pas Java; p Et s'il vous plaît, ne fournissez pas de code comme capture d'écran, ce n'est pas le bienvenu.Les éléments suivants échouent au test pour toutes les exceptions, cochées ou décochées:
la source
Vous pouvez créer n'importe quel type de vos propres assertions basées sur les assertions de junit:
Et test:
D'une manière générale, il est possible d'échouer instantanément ("bla bla bla") le test dans tous les scénarios, à tout endroit où cela a du sens. Par exemple, utilisez-le dans un bloc try / catch pour échouer si quelque chose est levé dans le cas de test:
Voici l'exemple de la méthode que nous testons, en supposant que nous avons une telle méthode qui ne doit pas échouer dans des circonstances spécifiques, mais elle peut échouer:
La méthode ci-dessus est un simple échantillon. Mais cela fonctionne pour des situations complexes, où l'échec n'est pas si évident. Il y a les importations:
la source