Comment écrivez-vous des tests pour du code qui dépend d'implémentations externes concrètes qui ne peuvent pas être moquées?

17

Contexte: Je pense essayer d'introduire le concept de tests unitaires à mes collègues en en créant pour un module sur lequel je travaille; les exigences de celui-ci ont récemment changé et nécessitent quelques abstractions / interactions supplémentaires, donc cela semble être un bon moyen de développer une suite de tests qui "prouveront" que cela fonctionne sans avoir à fouiller manuellement l'application.

Le problème, cependant, est que le module repose sur des facteurs externes non modifiables, à savoir les fichiers PDF et XSL. Fondamentalement, je lis XML à partir de la base de données et y applique une transformation XSL, puis le convertis en PDF à l'aide d'une bibliothèque appelée ABCPDF. Ce PDF est ensuite fusionné avec un autre PDF basé sur un modèle statique. Je sais que je peux tester le XML et m'assurer que les valeurs sont correctes, mais de nombreux bugs et problèmes potentiels sont liés à l'affichage réel du document fini - par exemple, des minuties comme la longueur des chaînes de texte, où certaines zones HTML sont situé par rapport au document, etc. Est-il même possible de tester ces choses (je me rends compte que ce sont probablement des tests d'intégration ou .. le troisième type de test dont j'oublie le nom [pas les tests d'acceptation, l'autre type], et pas l' unité tests) car je ne peux pas, à ma connaissance, simuler un PDF facilement avant de le créer, puis de le lire ou de créer une chaîne HTML (c'est-à-dire du XML transformé) et de l'analyser à la main pour vérifier la présence de certaines cellules de tableau dans par rapport aux autres cellules du tableau.

Dans une situation comme celle-ci, dois-je me concentrer uniquement sur les tests unitaires pour m'assurer que les informations sont correctes et que je peux créer le PDF, ou les fusionner, ou quoi que ce soit et recourir à des tests manuels pour les problèmes d'affichage réels?

Wayne Molina
la source
6
"Facteurs externes immuables" est un indice que vous ne faites pas de tests unitaires en premier lieu. Cela signifie des tests d' intégration . Que voulez-vous faire? Des tests unitaires de votre code ou des tests d'intégration de cette chose composite ? Veuillez choisir l'un ou l'autre, car il est difficile de parler des deux en même temps.
S.Lott
2
Je n'achète pas "immuable". J'accepterai "que je ne sais pas me moquer", ce qui signifie simplement que votre vraie question est "comment puis-je me moquer de lui?".
Rein Henrichs
Probablement :) Je suis familier avec la moquerie du XML utilisé, mais pas avec la façon de se moquer d'un vrai PDF ou d'un document HTML où la mise en forme est importante.
Wayne Molina
1
Je pense que vous voulez dire des tests "fonctionnels" (application de bout en bout) ou "système" (applications multiples de bout en bout)
Gary Rowe
@Gary - Oui, fonctionnel était le mot. Je m'en souviens maintenant de l'apprentissage de Rails: modèles de tests unitaires, contrôleurs de tests fonctionnels, tests d'intégration tout.
Wayne Molina

Réponses:

13

Testez la fonction et non l'unité

En utilisant des entrées xml connues, sortez un PDF et vérifiez manuellement (et méticuleusement) qu'il est correct. Enregistrez-le ensuite comme référence.

Les futurs tests utilisant les mêmes entrées xml peuvent faire une comparaison de fichiers binaires avec la référence.

Si une comparaison au niveau du fichier n'est pas satisfaisante, affichez le PDF à la fin du test et prenez des captures d'écran, puis comparez le test automatisé aux captures d'écran de référence.

Steven A. Lowe
la source
+1 parce que vous êtes uniquement intéressé par le résultat final à ce niveau. Si vous modifiez la mise en œuvre de la façon dont vous arrivez à obtenir le PDF, vous ne devriez pas avoir à modifier votre test fonctionnel.
Gary Rowe
2
+1 pour de bons conseils, c'est ce que nous faisons dans notre projet actuel. Nous avons créé un ensemble d'outils personnalisés pour effectuer la comparaison PDF, ce qui nous permet d'omettre les parties changeantes dans les documents tels que les horodatages. Attention: le passage à un autre (version du) rendu PDF peut entraîner des changements subtils dans la mise en page, provoquant une comparaison binaire directe pour signaler des tas de faux positifs.
Péter Török
5

Normalement, dans un cas comme celui-ci, vous résumez tout ce que vous ne pouvez pas tester derrière une implémentation que vous pouvez utiliser avec une interface. Je vais juste faire quelque chose de stupide comme PDF Builder car cela semble raisonnable.

public class PdfBuilder : IPdfBuilder
{
  public byte[] BuildPdf(...)
  {
    // actual untestable code here
  }
}

public interface IPdfBuilder
{
  byte[] BuildPdf(...);
}

Vous pouvez ensuite vous moquer de IPdfBuilder dans vos tests pour faire ce que vous voulez. Cela signifie souvent que vous devez commencer à utiliser un conteneur IoC ( /programming/871405/why-do-i-need-an-ioc-container-as-opposed-to-straightforward-di-code et /programming/21288/which-net-dependency-injection-frameworks-are-worth-looking-into comme point de départ) si vous n'en utilisez pas maintenant.

Et les tests qui ne sont pas des tests unitaires sont souvent appelés tests d'intégration. Les tests d'intégration compliqués n'en valent souvent pas la peine, vous devez donc simplement résumer cette partie et réduire la quantité de logique métier dans cette abstraction afin de pouvoir la tester dans un test unitaire.

Faites-moi savoir si ce n'est pas totalement clair.

Travis
la source
+1 pour masquer le code non testable. Ensuite, vous pouvez faire des tests manuels jusqu'à ce que vous trouviez ce qui doit traverser cette interface pour obtenir le bon résultat, et un test unitaire pour que cela soit généré correctement pour obtenir vos tests unitaires de régression.
Ethel Evans
1

J'ai construit quelque chose de très similaire il y a quelque temps et j'ai simplement utilisé des tests visuels de base. Les tests ne doivent pas être automatisés, il n'y a donc rien de mal à simplement rechercher un résultat attendu (évidemment, dans une variété de situations prédéterminées). Souvent, une image vaut mille tests en ce qui concerne les visuels . J'utilise intensivement les tests unitaires automatisés, mais je pense que certaines personnes peuvent être un peu emportées lorsqu'elles se lancent dans les tests GUI, ou n'importe quoi à mon humble avis. Avec certains produits, je reconnais que cette approche "assez bonne" ne sera pas suffisante donc YMMV.

Je serais cependant un peu inquiet des externalités immuables. Cela peut être un signe de couplage serré, ce qui est bon à éviter en règle générale, mais je ne spéculerai pas trop sur votre code à cet égard sans plus de détails.

Morgan Herlocker
la source
Il est très étroitement couplé, mais c'est un domaine que je ne peux pas résoudre car il n'y a pas d'adhésion pour le rendre faiblement couplé et aucune ressource n'est consacrée à la refactorisation (mais c'est un ensemble de problèmes complètement différent).
Wayne Molina