Je travaille avec le système suivant:
Network Data Feed -> Third Party Nio Library -> My Objects via adapter pattern
Nous avons récemment eu un problème concernant l’actualisation de la version de la bibliothèque que j’utilisais, ce qui a notamment provoqué la long
modification de l’ horodatage (renvoyé par la bibliothèque tierce ) de millisecondes après l’époque à des nanosecondes après l’époque.
Le problème:
Si j'écris des tests qui simulent les objets de la bibliothèque tierce, mon test sera faux si j'ai commis une erreur à propos des objets de la bibliothèque tierce. Par exemple, je ne savais pas que les horodatages changeaient la précision, ce qui nécessitait une modification du test unitaire, car ma maquette renvoyait les mauvaises données. Ce n'est pas un bug dans la bibliothèque , c'est parce que quelque chose dans la documentation m'a échappé.
Le problème est que je ne peux pas être sûr des données contenues dans ces structures de données car je ne peux pas en générer de vraies sans un véritable flux de données. Ces objets sont volumineux et compliqués et contiennent beaucoup de données différentes. La documentation de la bibliothèque tierce est médiocre.
La question:
Comment puis-je configurer mes tests pour tester ce comportement? Je ne suis pas sûr de pouvoir résoudre ce problème dans un test unitaire, car le test lui-même peut facilement être faux. De plus, le système intégré est volumineux et compliqué et il est facile de rater quelque chose. Par exemple, dans la situation ci-dessus, j’avais correctement ajusté la gestion de l’horodatage à plusieurs endroits, mais j’en ai manqué un. Le système semblait faire essentiellement les bons choix dans mon test d'intégration, mais lorsque je l'ai déployé en production (qui contient beaucoup plus de données), le problème est devenu évident.
Je n'ai pas de processus pour mes tests d'intégration pour le moment. Le test consiste essentiellement à: maintenir les tests unitaires correctement, ajouter d'autres tests en cas de défaillance, puis déployer sur mon serveur de test et s'assurer que les choses semblent saines, puis passer à la production. Ce problème d'horodatage a réussi les tests unitaires, car les modifications ont été créées, puis il a réussi le test d'intégration, car il ne posait aucun problème immédiat et évident. Je n'ai pas de département QA.
Timestamp
classe (contenant une représentation qu'ils veulent) et fournir des méthodes nommées (.seconds()
,.milliseconds()
,.microseconds()
,.nanoseconds()
) et des constructeurs cours nommés. Ensuite, il n'y aurait pas eu de problèmes.Réponses:
On dirait que vous faites déjà preuve de diligence raisonnable. Mais ...
Au niveau le plus pratique, incluez toujours dans votre suite une bonne poignée de tests d'intégration "en boucle complète" pour votre propre code, et écrivez plus d'assertions qu'il ne vous en faut. En particulier, vous devriez avoir une poignée de tests qui effectuent un cycle complet create-read- [do_stuff] -validate.
Et on dirait que vous faites déjà ce genre de chose. Vous ne faites que traiter avec une bibliothèque feuilletée et / ou compliquée. Et dans ce cas, il est bon d'ajouter quelques types de tests "voici comment fonctionne la bibliothèque" qui vérifient à la fois votre compréhension de la bibliothèque et servent d'exemples d'utilisation de la bibliothèque.
Supposons que vous deviez comprendre et dépendre de la manière dont un analyseur JSON interprète chaque "type" dans une chaîne JSON. Il est utile et trivial d'inclure quelque chose comme ceci dans votre suite:
Mais deuxièmement, rappelez-vous que les tests automatisés de tous types et à n'importe quel niveau de rigueur ne pourront toujours pas vous protéger contre tous les bogues. Il est parfaitement courant d'ajouter des tests au fur et à mesure que vous découvrez des problèmes. N'ayant pas de service d'assurance qualité, cela signifie que beaucoup de ces problèmes seront découverts par les utilisateurs finaux.
Et dans une large mesure, c'est juste normal.
Et troisièmement, quand une bibliothèque change la signification d'une valeur de retour ou d'un champ sans renommer le champ ou la méthode, ni autrement "casser" le code dépendant (peut-être en changeant son type), je serais vraiment mécontent de cet éditeur. Et je dirais que, même si vous auriez probablement dû lire le journal des modifications s'il en existe un, vous devriez probablement également transmettre une partie de votre stress à l'éditeur. Je dirais qu'ils ont besoin de la critique constructive, espérons-le ...
la source
(new JSONParser()).parse(datastream)
, car ils récupèrent les données directement à partir deNetworkInterface
et toutes les classes qui effectuent l'analyse réelle sont des paquetages privés et proguardés.NetworkInterface
... Est-ce quelque chose que vous pouvez alimenter en données en connectant l'interface à un port sur localhost ou quelque chose du genre?NetworkInterface
. C’est un objet de bas niveau pour travailler directement avec une carte réseau et y ouvrir des sockets, etc.Réponse courte: c'est difficile. Vous avez probablement l'impression qu'il n'y a pas de bonne réponse, et c'est parce qu'il n'y a pas de réponse facile.
Réponse longue: Comme le dit @ptyx , vous avez besoin de tests du système, de tests d'intégration et de tests unitaires:
Quelques suggestions spécifiques:
J'ai vu la programmation décrite comme l'activité d'apprentissage d'un problème et d'un espace de solution. Avoir tout parfait à l'avance peut ne pas être réalisable, mais vous pouvez apprendre après coup. ("J'ai corrigé le traitement de l'horodatage à plusieurs endroits mais j'en ai oublié un. Puis-je modifier mes types de données ou mes classes pour rendre le traitement de l'horodatage plus explicite et plus difficile à manquer, ou pour le rendre plus centralisé afin que je ne dispose que d'un seul emplacement? Puis-je modifier Puis-je simplifier mon environnement de test pour le rendre plus facile à l'avenir? Puis-je imaginer un outil qui l'aurait facilité et, dans l'affirmative, puis-je le trouver sur Google? " Etc.)
la source
Je suis fortement en désaccord avec vous ici. C'est un bug dans la bibliothèque , plutôt insidieux en fait. Ils ont changé le type sémantique de la valeur de retour, mais pas le type de programmation de la valeur de retour. Cela peut causer toutes sortes de ravages, surtout s’il s’agissait d’une bosse de version mineure, mais même s’il s’agissait d’une bombe majeure.
Disons plutôt que la bibliothèque a retourné un type de
MillisecondsSinceEpoch
, un simple wrapper contenant unlong
. Quand ils ont changé uneNanosecondsSinceEpoch
valeur, votre code aurait échoué à la compilation et vous aurait évidemment indiqué les endroits où vous devez apporter des modifications. Le changement ne pouvait pas corrompre votre programme en silence.Mieux encore, ce serait un
TimeSinceEpoch
objet qui pourrait adapter son interface à mesure que davantage de précision était ajoutée, telle que l'ajout d'une#toLongNanoseconds
méthode à côté de la#toLongMilliseconds
méthode, ne nécessitant aucune modification de votre code.Le problème suivant est que vous ne disposez pas d’un ensemble fiable de tests d’intégration dans la bibliothèque. Vous devriez écrire ceux-ci. Il serait préférable de créer une interface autour de cette bibliothèque pour l’encapsuler loin du reste de votre application. Plusieurs autres réponses abordent ce problème (et d’autres continuent à apparaître au fur et à mesure que je tape). Les tests d'intégration doivent être exécutés moins souvent que vos tests unitaires. C'est pourquoi avoir une couche tampon aide. Séparez vos tests d'intégration dans une zone distincte (ou nommez-les différemment) afin de pouvoir les exécuter selon vos besoins, mais pas à chaque fois que vous exécutez votre test unitaire.
la source
...Ex()
méthodes de Win32API). Si ce n'était pas faisable, "rompre" le contrat en renommant la fonction (ou son type de retour) aurait été préférable à une modification du comportement.Vous avez besoin de tests d'intégration et de système.
Les tests unitaires permettent de vérifier que votre code se comporte comme prévu. Comme vous le réalisez, cela ne remet pas en question vos hypothèses et ne garantit pas la santé de vos attentes.
Sauf si votre produit a peu d'interaction avec des systèmes externes, ou interagit avec des systèmes si bien connus, stables et documentés qu'ils peuvent être simulés avec assurance (cela se produit rarement dans le monde réel) - les tests unitaires ne suffisent pas.
Plus vos tests sont élevés, plus ils vous protégeront des imprévus. Cela a un coût (commodité, rapidité, fragilité ...), donc les tests unitaires doivent rester la base de vos tests, mais vous avez besoin d'autres couches, y compris - éventuellement - un petit peu de tests humains qui contribueront grandement à capturer Des choses stupides auxquelles personne n'a pensé.
la source
Le mieux serait de créer un prototype minimal et de comprendre le fonctionnement exact de la bibliothèque. En faisant cela, vous gagnerez des connaissances sur la bibliothèque avec une documentation médiocre. Un prototype peut être un programme minimaliste qui utilise cette bibliothèque et assure la fonctionnalité.
Sinon, cela n'a aucun sens d'écrire des tests unitaires, avec des exigences à moitié définies et une compréhension faible du système.
En ce qui concerne votre problème spécifique - à propos de l’utilisation d’indicateurs erronés: je le traiterais comme un changement d’exigences. Une fois le problème reconnu, modifiez les tests unitaires et le code.
la source
Si vous utilisiez une bibliothèque populaire et stable, alors vous pourriez peut-être supposer qu'elle ne vous jouera pas de mauvais tours. Mais si des choses comme ce que vous avez décrit se produisent avec cette bibliothèque, alors évidemment, ce n'en est pas une. Après cette mauvaise expérience, chaque fois que quelque chose ne va pas dans votre interaction avec cette bibliothèque, vous devrez examiner non seulement la possibilité que vous ayez commis une erreur, mais également la possibilité que la bibliothèque ait commis une erreur. Donc, disons qu'il s'agit d'une bibliothèque sur laquelle vous n'êtes "pas sûr".
Une des techniques employées avec les bibliothèques sur lesquelles nous ne sommes "pas sûrs" est de construire une couche intermédiaire entre notre système et lesdites bibliothèques, qui fait abstraction de la fonctionnalité offerte par les bibliothèques, affirme que nos attentes vis-à-vis de la bibliothèque sont justes et qu'elle simplifie grandement notre vie à l'avenir, devrions-nous décider de donner à cette bibliothèque le démarrage et de la remplacer par une autre bibliothèque qui se comporte mieux.
la source
assert
mot-clé (ou fonction, ou fonction, en fonction de la langue que vous utilisez), est votre ami. Je ne parle pas d'assertions dans les tests unitaires / d'intégration, je dis que la couche d'isolation devrait être très lourde d'assertions, affirmant tout ce qui peut être affirmé à propos du comportement de la bibliothèque.