Nous avons construit un nombre substantiel de tests unitaires pour notre programme principal au fil des ans. Plusieurs milliers. Le problème est que nous n'avons pas une idée claire de quels tests nous avons parce qu'il y en a tellement. Et c'est un problème parce que nous ne savons pas où nous sommes faibles sur les tests (ou où nous avons des doublons).
Notre application est un moteur de reporting. Vous pouvez donc avoir un modèle qui est utilisé pour tester l'analyse (lisons-nous toutes les propriétés de la table), fusionnant les données (avons-nous conservé les bonnes propriétés de la table dans la fusion), formatant la page finale (la table est-elle placée correctement sur la page ) et / ou le format de sortie (le fichier DOCX créé est-il correct).
Ajoutez à cela ce que nous devons tester. Prenez le remplissage autour d'une cellule de tableau (nous utilisons Word, Excel et PowerPoint pour la conception du rapport). Nous devons tester le remplissage entre les sauts de page, pour un tableau à l'intérieur d'une cellule, des cellules fusionnées verticalement, des cellules fusionnées horizontalement, une cellule fusionnée verticalement et horizontalement qui contient un tableau avec des cellules fusionnées verticalement et horizontalement dans le tableau interne, où ce tableau traverse une page.
Alors, dans quelle catégorie ce test entre-t-il? Remplissage de tableau, sauts de page, cellules imbriquées, cellules fusionnées verticalement, cellules fusionnées horizontalement ou autre chose?
Et comment documenter ces catégories, nommer les tests unitaires, etc.?
Mise à jour: Un certain nombre de personnes ont suggéré d'utiliser des outils de couverture pour vérifier que nous avons une couverture complète. Malheureusement, cela est d'une utilité limitée dans notre cas, car les bogues ont tendance à être dus à des combinaisons spécifiques, c'est donc le code qui a tous été testé, mais pas dans cette combinaison.
Par exemple, un client hier a commencé un signet Word à la fin d'une boucle forEach dans son modèle (un document Word) et l'a terminé au début de la boucle forEach suivante. Tout cela utilisait du code qui a des tests unitaires, mais nous n'avions pas pensé à la combinaison d'un modèle développant un signet commençant à être démarré 25 fois, puis terminé 10 fois (les deux boucles forEach avaient un nombre différent de lignes).
la source
Réponses:
En général, j'ai tendance à refléter l'arborescence source pour mes tests unitaires. Donc, si j'avais src / lib / fubar, j'aurais un test / lib / fubar qui contiendrait les tests unitaires pour fubar.
Cependant, ce que vous semblez décrire, ce sont des tests plus fonctionnels. Dans ce cas, j'aurais un tableau multidimensionnel qui énumérerait toutes vos conditions possibles. Ensuite, ceux qui n'ont aucun test sont soit absurdes, soit nécessitent un nouveau test. Vous pouvez bien sûr les placer ensuite dans des ensembles de suites de tests.
la source
La structure la plus courante semble être le miroir de répertoires
src
andtest
.Cependant, il y a une structure alternative que j'ai vue et que je pense maintenant meilleure.
Étant donné que le test unitaire a tendance à refléter la classe qu'ils testent, le fait de les placer dans le même répertoire permet un accès beaucoup plus facile aux fichiers afin que vous puissiez travailler côte à côte.
la source
Dans .NET, j'ai tendance à refléter, ou du moins à approximer, la structure de l'espace de noms pour le code source dans les projets de test, sous un espace de noms principal "Tests.Unit" ou "Tests.Integration". Tous les tests unitaires vont dans un projet, avec la structure de base du code source répliquée sous forme de dossiers dans le projet. Idem pour les tests d'intégration. Ainsi, une solution simple pour un projet pourrait ressembler à ceci:
Pour tout AAT ou AEET codé avec un cadre de test unitaire, cela change un peu; généralement, ces tests reflètent un ensemble d'étapes, qui testeront la fonctionnalité d'un nouveau cas d'utilisation ou d'une nouvelle histoire. Je structure généralement ces tests dans un
MyProduct.Tests.Acceptance
projet en tant que tel, avec des tests pour chaque histoire, éventuellement regroupés par étape ou histoire "épique" à laquelle appartient l'histoire en cours de développement. Cependant, ce ne sont vraiment que des tests d'intégration, et donc si vous préférez structurer les tests d'une manière plus orientée objet plutôt que narrative, vous n'avez même pas besoin d'unMyProduct.Tests.Acceptance
projet ou d'un projet similaire; jetez-les simplementMyProduct.Tests.Integration
sous la portée de l'objet de plus haut niveau testé.la source
Il n'y a aucune raison pour qu'un test unitaire soit dans une seule catégorie. Toutes les principales boîtes à outils de tests unitaires prennent en charge la création de suites de tests , qui regroupent les tests pour une catégorie particulière. Lorsqu'une zone de code particulière a été modifiée, le développeur doit exécuter cette suite en premier et souvent pour voir ce qui a cassé. Lorsqu'un test concerne le remplissage , les ruptures et l' imbrication, mettez-le dans les trois suites.
Cela dit, le but des tests unitaires est de les exécuter tout le temps, c'est-à-dire qu'ils doivent être petits et suffisamment rapides pour qu'il soit possible de les exécuter tous avant de commettre n'importe quel code. En d'autres termes, peu importe la catégorie d'un test, il doit être exécuté avant de s'engager de toute façon. Les suites ne sont vraiment qu'une béquille que vous utilisez si, pour une raison quelconque, vous ne pouvez pas écrire des tests aussi rapides qu'ils le devraient.
En ce qui concerne la couverture, il existe de très bons outils de couverture qui vous indiquent le pourcentage de lignes réellement exercées lors de l'exécution de vos tests - c'est un indicateur évident du type de tests qui vous manque encore.
Quant à la dénomination, il n'y a pas de valeur particulière à consacrer des efforts aux noms des tests unitaires. Tout ce que vous voulez entendre de vos tests est "5235 des 5235 tests réussis". Lorsqu'un test échoue, ce que vous lisez n'est pas son nom, mais le message , par exemple la chaîne dans le
assert()
qui implémente votre critique de réussite. Le message doit être suffisamment informatif pour que vous ayez une idée de ce qui ne va pas sans lire le corps du test. Le nom n'a pas d'importance par rapport à cela.la source
La traçabilité est un moyen de savoir si vous êtes faible aux tests. Habituellement, pour les tests, cela prend la forme d'une couverture.
Le but est de mesurer quelles parties du code sont exercées par vos tests, afin que vous puissiez voir le code qui n'est pas couvert par vos tests. C'est à vous (et à l'outil de couverture) de définir ce qu'est une "partie de code". Le moins est la couverture des succursales.
la source