Si vous ne devez avoir qu'une seule assertion par test; comment tester plusieurs entrées?

15

J'essaie de créer des cas de test et j'ai lu que vous devriez essayer de limiter le nombre d'assertions par cas de test.

Ma question est donc de savoir quelle est la meilleure façon de tester une fonction avec plusieurs entrées. Par exemple, j'ai une fonction qui analyse une chaîne de l'utilisateur et renvoie le nombre de minutes. La chaîne peut être sous la forme "5w6h2d1m", où w, h, d, mcorrespond au nombre de semaines, heures, jours et minutes.

Si je voulais suivre la «1 assertion par règle de test», devrais-je faire plusieurs tests pour chaque variation d'entrée? Cela semble idiot, alors j'ai juste quelque chose comme:

self.assertEqual(parse_date('5m'), 5)
self.assertEqual(parse_date('5h'), 300)
self.assertEqual(parse_date('5d') ,7200)
self.assertEqual(parse_date('1d4h20m'), 1700)

Dans le seul cas de test. Y a-t-il une meilleure façon?

speg
la source
La meilleure façon de le faire est d'utiliser des paramètres (certains frameworks supportent cette fonctionnalité, tous les frameworks devraient). De cette façon, vous testez un seul comportement mais en tenant compte de nombreux cas de test et vous pouvez toujours voir quelles valeurs de paramètre ont causé une erreur si une erreur se produit
Kemoda

Réponses:

23

Une façon plus pragmatique de visualiser la seule règle par assertion par test consiste à faire en sorte que vos assertions dans un seul test couvrent un seul concept.

De cette façon, vous testez toujours un seul concept dans un seul test. Dans votre cas, si votre chaîne d'entrée est correctement analysée en une seule date.

Vous devriez exercer votre jugement au cas par cas pour vérifier si vous feriez mieux de tester un concept unique avec plusieurs assertions ou d'avoir une seule assertion dans plusieurs tests.

Optez pour l'option qui permet des tests plus clairs, moins de répétitions tout en permettant à vos tests de mettre en évidence différents points d'échecs dans votre méthode. Vous voulez qu'il soit clair lorsqu'un test échoue exactement ce qui s'est passé plutôt que d'avoir à déboguer votre test pour découvrir ce qui s'est mal passé.

Gilles
la source
17

Cela dépend beaucoup de votre bibliothèque de tests. Dans la bibliothèque C # NUnit, vous pouvez faire quelque chose comme:

[TestCase('5m', 5)]
[TestCase('5h', 300)]
[TestCase('5d', 7200)]
[TestCase('1d4h20m', 1700)]
public void ParseDateTest(inputString, expectedMinutes)
{
    Assert.That(parse_date(inputString), Is.EqualTo(expectedMinutes));
}
Scroog1
la source
En java avec testng, vous avez des méthodes
@DataProvider
c'est la meilleure solution à mon humble avis. dans presque toutes les langues, vous pouvez paramétrer vos tests. pour java: @Parameterized , JunitParams , Zohhak
piotrek
3

Oui, faites plusieurs tests pour chaque variation d'entrée.

L'objectif principal d'une assertion par directive de test est de disposer (idéalement) qu'une erreur entraîne un échec de test et vice versa afin que vous sachiez exactement ce qui a échoué. Ensuite, vous pouvez travailler avec un test très précis pour déboguer la cause racine et vérifier. Vous pouvez rompre cela avec une assertion et vous pouvez être d'accord avec plusieurs assertions. Dans ce scénario particulier, j'aurais un test pour chaque suffixe et quelques combinaisons de commande.

Avec un peu de chance, il est clair pourquoi l'isolation des tests est un avantage: vous perdez moins de temps à déboguer en cas de problème. Maintenant, si vous êtes vraiment sûr qu'il est peu probable que le test échoue et que les frais généraux liés à la recherche à travers le test soient faibles, il est peut-être judicieux de les tester tous à la fois pour gagner du temps de mise en œuvre.

Mais l'histoire a montré que gagner un peu de temps à écrire du code au détriment de la lecture / utilisation du code n'en vaut jamais la peine. D'où la ligne directrice.

Telastyn
la source
1

Les puristes diraient que les assertions pour différentes valeurs d'entrée devraient être placées dans des méthodes de test distinctes au sein de la classe de test. L'une des raisons en est que, selon votre interface utilisateur de test, il est souvent plus facile de faire la distinction entre les échecs de test individuels qu'entre les assertions individuelles, ce qui pourrait vous amener à identifier plus rapidement la source de l'échec.

Lorsque vous testez avec JUnit, nous pouvons contourner ce problème en utilisant la version des méthodes assert * avec un argument String initial pour différencier une assertion d'une autre au sein de la même méthode de test.

self.assertEqual("just minutes", parse_date('5m'), 5)
self.assertEqual("just hours", parse_date('5h'), 300)
self.assertEqual("just days", ('5d') ,7200)
self.assertEqual("days, hours, minutes", parse_date('1d4h20m'), 1700)
Mike Partridge
la source