Il y a deux questions que nous devons examiner ici.
La première est que vous semblez regarder tous vos tests du point de vue des tests unitaires. Les tests unitaires sont extrêmement précieux, mais ne sont pas les seuls types de tests. Les tests peuvent en fait être divisés en plusieurs couches différentes, des tests unitaires très rapides aux tests d' intégration moins rapides aux tests d' acceptation encore plus lents . (Il peut y avoir encore plus de couches éclatées, comme des tests fonctionnels .)
La seconde est que vous mélangez les appels à du code tiers avec votre logique métier, créant des défis de test et éventuellement rendant votre code plus fragile.
Les tests unitaires doivent être rapides et doivent être exécutés souvent. Les dépendances moqueuses permettent de maintenir ces tests en cours d'exécution rapidement, mais peuvent potentiellement introduire des trous dans la couverture si la dépendance change et que la maquette ne change pas. Votre code peut être rompu pendant que vos tests sont toujours verts. Certaines bibliothèques moqueuses vous alerteront si l'interface de la dépendance change, d'autres non.
Les tests d'intégration, d'autre part, sont conçus pour tester les interactions entre les composants, y compris les bibliothèques tierces. Les simulations ne doivent pas être utilisées à ce niveau de test car nous voulons voir comment l'objet réel interagit ensemble. Parce que nous utilisons des objets réels, ces tests seront plus lents et nous ne les exécuterons pas aussi souvent que nos tests unitaires.
Les tests d'acceptation regardent à un niveau encore plus élevé, testant que les exigences pour le logiciel sont remplies. Ces tests s'exécutent sur l'ensemble du système complet qui serait déployé. Encore une fois, aucune moquerie ne doit être utilisée.
Une ligne directrice que les gens ont trouvée utile concernant les simulateurs est de ne pas simuler les types que vous ne possédez pas . Amazon possède l'API de S3 afin qu'ils puissent s'assurer qu'elle ne change pas en dessous d'eux. En revanche, vous n'avez pas ces assurances. Par conséquent, si vous simulez l'API S3 dans vos tests, cela pourrait changer et casser votre code, tandis que tous vos tests sont verts. Alors, comment pouvons-nous tester le code unitaire qui utilise des bibliothèques tierces?
Et bien non. Si nous suivons la directive, nous ne pouvons pas nous moquer des objets que nous ne possédons pas. Mais… si nous possédons nos dépendances directes, nous pouvons nous en moquer. Mais comment? Nous créons notre propre wrapper pour l'API S3. Nous pouvons la faire ressembler beaucoup à l'API S3, ou nous pouvons l'adapter plus étroitement à nos besoins (préféré). On peut même le rendre un peu plus abstrait, disons un PersistenceService
plutôt qu'un AmazonS3Bucket
. PersistenceService
serait une interface avec des méthodes comme #save(Thing)
et #fetch(ThingId)
, les types de méthodes que nous aimerions voir (ce sont des exemples, vous voudrez peut-être en fait différentes méthodes). Nous pouvons maintenant implémenter un PersistenceService
autour de l'API S3 (disons a S3PersistenceService
), en l'encapsulant loin de notre code appelant.
Passons maintenant au code qui appelle l'API S3. Nous devons remplacer ces appels par des appels à un PersistenceService
objet. Nous utilisons l' injection de dépendance pour passer notre PersistenceService
dans l'objet. Il est important de ne pas demander un S3PersistenceService
, mais de demander un PersistenceService
. Cela nous permet d'échanger l'implémentation lors de nos tests.
Tout le code qui utilisait directement l'API S3 utilise désormais notre PersistenceService
, et notre S3PersistenceService
effectue désormais tous les appels à l'API S3. Dans nos tests, nous pouvons nous moquer PersistenceService
, puisque nous en sommes propriétaires, et utiliser la maquette pour nous assurer que notre code effectue les appels corrects. Mais maintenant, cela laisse comment tester S3PersistenceService
. Il a le même problème qu'auparavant: nous ne pouvons pas le tester à l'unité sans appeler le service externe. Donc… nous ne le testons pas à l'unité. Nous pourrions nous moquer des dépendances de l'API S3, mais cela nous donnerait peu ou pas de confiance supplémentaire. Au lieu de cela, nous devons le tester à un niveau supérieur: les tests d'intégration.
Cela peut sembler un peu troublant de dire que nous ne devrions pas tester à l'unité une partie de notre code, mais regardons ce que nous avons accompli. Nous avions un tas de code un peu partout, nous ne pouvions pas faire de tests unitaires qui peuvent maintenant être testés par le biais de PersistenceService
. Nous avons notre désordre de bibliothèque tiers limité à une seule classe d'implémentation. Cette classe doit fournir les fonctionnalités nécessaires pour utiliser l'API, mais n'a pas de logique métier externe attachée. Par conséquent, une fois écrit, il devrait être très stable et ne devrait pas changer beaucoup. Nous pouvons compter sur des tests plus lents que nous n'exécutons pas si souvent car le code est stable.
L'étape suivante consiste à écrire les tests d'intégration pour S3PersistenceService
. Ceux-ci doivent être séparés par nom ou dossier afin que nous puissions les exécuter séparément de nos tests unitaires rapides. Les tests d'intégration peuvent souvent utiliser les mêmes cadres de test que les tests unitaires si le code est suffisamment informatif, nous n'avons donc pas besoin d'apprendre un nouvel outil. Le code réel du test d'intégration est ce que vous écririez pour votre option 1.