J'essaie de tester une classe qui appelle certains services Web Hadoop. Le code est à peu près de la forme:
method() {
...use Jersey client to create WebResource...
...make request...
...do something with response...
}
par exemple, il existe une méthode de création de répertoire, une méthode de création de dossier, etc.
Étant donné que le code traite d'un service Web externe sur lequel je n'ai pas de contrôle, comment puis-je le tester à l'unité? Je pourrais essayer de me moquer du client du service Web / des réponses, mais cela rompt la directive que j'ai beaucoup vue récemment: "Ne vous moquez pas d'objets que vous ne possédez pas". Je pourrais mettre en place une implémentation de service Web factice - cela constituerait-il toujours un "test unitaire" ou serait-ce alors un test d'intégration? N'est-il tout simplement pas possible de faire des tests unitaires à un niveau aussi bas - comment un praticien TDD s'y prendrait-il?
la source
Réponses:
À mon avis, vous devriez vous moquer des appels du service Web s'il s'agit d'un test unitaire, par opposition à un test d'intégration.
Votre test unitaire ne doit pas tester si le service Web externe fonctionne ou si votre intégration avec celui-ci est correcte. Sans devenir trop dogmatique sur le TDD, notez qu'un effet secondaire de la transformation de votre test unitaire en test d'intégration est qu'il est susceptible de s'exécuter plus lentement, et vous voulez des tests unitaires rapides .
De plus, si le service Web est temporairement arrêté ou ne fonctionne pas correctement, cela devrait-il entraîner l'échec de votre test unitaire? Cela ne semble pas correct. Votre test unitaire devrait échouer pour une seule raison: s'il y a un bogue dans le code de cette "unité".
La seule partie de code pertinente ici est
...do something with response...
. Se moquer du reste.la source
Je ne suis pas d'accord avec "ne vous moquez pas d'objets que vous ne possédez pas" lorsque vous effectuez des tests unitaires.
Le but de l'existence des moqueries est le fait qu'il y aura des modules, des bibliothèques, des classes que nous ne posséderons pas.
Ma suggestion pour votre scénario est de se moquer de l'appel du service Web.
Configurez la maquette de manière à ce qu'elle renvoie les données à votre module.
Assurez-vous de couvrir tous les scénarios, par exemple lorsque les données renvoyées sont nulles, lorsque les données renvoyées sont valides, etc.
Et pour le code que vous possédez, votre responsabilité en tant que développeur est de vous assurer que le code que vous créez fonctionne comme prévu dans tous les scénarios.
la source
J'utiliserais quelque chose comme EasyMock pour ce test. Les frameworks de simulation sont un moyen idéal pour supprimer les dépendances extérieures sur une classe et vous donnent un contrôle total sur le résultat des dépendances extérieures pendant les tests. Pour prolonger un peu votre exemple:
La première chose que vous devez faire est d'extraire la logique de votre classe où vous utilisez Jersey pour obtenir une ressource Web et appeler le service Web dans une classe distincte. La création d'une interface pour cette classe vous permettra de créer une maquette à laquelle vous pourrez ensuite dicter le comportement.
Une fois cette interface créée, vous pouvez créer une maquette à l'aide d'EasyMock, qui renverra un objet spécifié selon votre scénario de test. L'exemple ci-dessus est une simplification de la façon de structurer un test simulé de base et du fonctionnement de votre interface.
Pour plus d'informations sur les frameworks de simulation, veuillez consulter cette question . De plus, cet exemple suppose l'utilisation de Java mais les frameworks de simulation sont disponibles dans tous les langages et bien qu'ils soient implémentés différemment, ils fonctionneront généralement de la même manière
la source
Les simulacres sont acceptables dans ce cas, mais vous n'en avez pas besoin. Au lieu de tests unitaires, testez à la
method()
place uniquement la partie qui gère la réponse.Extraire une fonction qui prend
ResponseData
(quel que soit le type approprié) puis exécute l'action.Au lieu de vous moquer, il vous suffit maintenant de construire un objet ResponseData et de le transmettre.
Vous pouvez laisser l' appel du service à des tests d'intégration complets - qui couvriront
method()
au totalla source
Ce que j'ai fait et ça marche:
3.1 Tout d'abord, tous les services Web sont testés. De chaque machine, même les machines du développeur. Ce sont les vrais webservices, mais fonctionnant en environnement de développement. Cela signifie que les services Web ne peuvent jamais être interrompus ou répondre à des valeurs erronées, car sinon, chaque développeur se plaint de ne pas pouvoir compiler.
3.2 Ensuite, tous les tests unitaires internes à l'application sont exécutés. Cela signifie que tous les services Web sont simulés et testés en exécutant les mêmes tests que 3.1 (et qu'ils devraient également passer, sinon les simulations sont fausses), et être invoquées par l'application réelle comme si elles étaient réellement utilisées. Si les simulations sont erronées, vous pouvez exécuter le test en 3.1 et enregistrer ces valeurs (demande, réponse) dans un HashMap.
3.3 Ensuite, les mêmes tests que 3.2 sont exécutés, mais cette fois contre les vrais services web fonctionnant dans l'environnement de développement.
Une fois tous ces éléments terminés, pour l'environnement de production réel, il vous suffit de fournir l'adresse réelle de chaque service Web. Espérons que cela ne nécessite pas trop de changements dans la configuration.
la source