Est-ce une bonne idée d'avoir des méthodes de test distinctes pour chaque étape?


10

Je teste une API REST. Disons qu'il renvoie une structure JSON. Quelle est la meilleure approche pour tester le serveur? Chaque étape de test ne peut réussir que si toutes les précédentes ont réussi.

Structure A: testez tout à la fois

- Test method 1:
    - make server request
    - assert http response code was 200
    - assert returned file is not empty
    - assert returned file has valid JSON syntax
    - assert returned JSON contains key X

Cela semble être la meilleure solution.

Avantages:

  • Une seule demande de serveur
  • Je teste le comportement dans son ensemble "Le serveur renvoie-t-il un JSON avec la clé X?"

Structure B: ajouter progressivement des assertions à chaque test

 - Test method 1:
     - make server request
     - assert http response code was 200
 - Test method 2:
     - make server request
     - assert returned file is not empty
 - Test method 3:
     - make server request
     - assert returned file has valid JSON syntax
 - Test method 4:
     - make server request
     - assert returned JSON contains key X

C'est ainsi que j'ai commencé à le faire et j'étais convaincu que cela devrait être la voie à suivre car chaque méthode ne teste qu'une seule chose et cela crée une meilleure séparation. Mais maintenant, je pense que, puisqu'il ne s'agit pas de tests unitaires, ma séparation n'est pas appropriée et que je devrais tester le comportement dans son ensemble.

Structure C: faire une demande une fois et exécuter des méthodes de test distinctes sur la réponse mise en cache

- make server request and cache it (allow read-only access)

 - Test method 1:
     - assert http response code was 200 on cached server request
 - Test method 2:
     - assert returned file is not empty on cached server request
 - Test method 3:
     - assert returned file has valid JSON syntax on cached server request
 - Test method 4:
     - assert returned JSON contains key X on cached server request

Avantages:

  • Aucune demande de serveur répétée (coûteuse)
  • A toujours des méthodes de test à assertion unique

Quelle est la structure de test la plus judicieuse à utiliser?


Veuillez arrêter de modifier votre question par la suite d'une manière qui invalide les réponses existantes! Merci.
Doc Brown

Désolé de vous avoir dérangé, mais proposeriez-vous de faire autrement?
mrplow

Tout d'abord, réfléchissez-y à deux fois si vous devez vraiment changer votre question de cette manière. Si vous pensez vraiment que vous devez ajouter quelque chose qui invalide certaines réponses, vous pouvez informer tous les auteurs de ces réponses en laissant un commentaire sous leur réponse en leur demandant s'ils veulent changer ou ajouter quelque chose dans leur texte.
Doc Brown

2
J'ai en fait supposé que les auteurs des réponses SONT notifiés si la question était modifiée. C'est pourquoi je ne voulais pas spammer les commentaires avec des déclarations hors sujet. J'informerai les auteurs à l'avenir. Et merci d'avoir répondu à ma question.
mrplow

Réponses:


3

Les meilleures pratiques ont toujours un but, une raison derrière elles. C'est toujours une bonne idée de tenir compte de ces raisons dans votre conception - en particulier lorsque vous essayez de décider comment et à quel point suivre ces meilleures pratiques.

Dans ce cas, le principal raisonnement derrière le fait de faire de chaque test une seule chose est que si la première chose échoue, la seconde ne sera pas testée. Étant donné que trop de faiseurs d'opinion semblent trouver utile de tout casser dans les moindres bits possibles et d'envelopper chaque bit dans le plus de ballonnement possible, cela a donné naissance à l'idée que chaque test devrait contenir une seule assertion.

Ne suivez pas cela aveuglément. Même si chaque test doit tester une chose, vous devez quand même réfléchir à la taille de chaque "chose", et pour ce faire, vous devez garder à l'esprit pourquoi vous voulez que chaque test teste une chose - pour vous assurer un bug dans la première chose ne laisse pas la deuxième chose non testée.

Donc, vous devez vous demander - "ai-je vraiment besoin de cette garantie ici?"

Disons qu'il y a un bogue dans le premier cas de test - le code de réponse HTTP ne l'est pas 200. Vous commencez donc à pirater le code, découvrez pourquoi vous n'avez pas obtenu le code de réponse que vous devriez avoir et corrigez le problème. Et maintenant?

  • Si vous réexécutez manuellement le test, pour vérifier que votre correctif a résolu le problème, vous devez rencontrer tout autre problème masqué par le premier échec.
  • Si vous ne l'exécutez pas manuellement (peut-être parce que cela prend trop de temps?), Et que vous appuyez simplement sur votre correctif en attendant que le serveur de tests automatisés exécute tout, vous souhaiterez peut-être placer différentes assertions dans différents tests. Les cycles dans ce cas sont très longs, il vaut donc la peine de faire l'effort de découvrir autant de bugs dans chaque cycle.

Il y a quelques autres choses à considérer:

Dépendances d'assertions

Je sais que les tests que vous avez décrits ne sont qu'un exemple, et vos tests réels sont probablement plus compliqués - donc ce que je vais dire peut ne pas être valable avec autant de force dans les vrais tests, mais il peut quand même être quelque peu efficace pour que vous peut vouloir le considérer.

Si vous avez un service REST (ou tout autre protocole HTTP) qui renvoie des réponses au format JSON, vous écrivez généralement une classe client simple qui vous permet d'utiliser les méthodes REST comme des méthodes régulières qui renvoient des objets normaux. En supposant que le client a des tests séparés pour s'assurer qu'il fonctionne, j'aurais abandonné les 3 premiers assertions et n'en garderais que 4!

Pourquoi?

  • La première assertion est redondante - la classe cliente doit lever une exception si le code de réponse HTTP n'est pas 200.
  • La deuxième assertion est redondante - si la réponse est vide, l'objet résultat sera nul ou une autre représentation d'un objet vide, et vous n'aurez nulle part où mettre la clé X.
  • La troisième assertion est redondante - si le JSON n'est pas valide, vous obtiendrez une exception lorsque vous essayez de l'analyser.

Vous n'avez donc pas besoin d'exécuter tous ces tests - exécutez simplement le quatrième test, et si l'un des bogues que les trois premiers essaient de détecter se produit, le test échouera avec une exception appropriée avant même d'obtenir l'assertion réelle.

Comment souhaitez-vous recevoir les rapports?

Supposons que vous n'obteniez pas d'e-mails d'un serveur de test, mais que le service QA exécute les tests et vous informe des tests ayant échoué.

Jack de QA frappe à votre porte. Il dit que la première méthode de test a échoué et que la méthode REST a renvoyé un mauvais code de réponse. Vous le remerciez et commencez à chercher la cause profonde.

Vient ensuite Jen de QA et dit que la troisième méthode de test a échoué - la méthode REST n'a pas renvoyé un JSON valide dans le corps de la réponse. Vous lui dites que vous examinez déjà cette méthode, et vous pensez que la même chose qui l'a fait retourner un mauvais code de sortie l'a également fait renvoyer quelque chose qui n'est pas un JSON valide, et ressemble plus à une trace de pile d'exceptions.

Vous vous remettez au travail, mais Jim de QA arrive, disant que la quatrième méthode de test a échoué et qu'il n'y a pas de clé X dans la réponse ...

Vous ne pouvez même pas chercher la raison, car il est difficile de regarder le code lorsque vous n'avez pas d'écran d'ordinateur. Si Jim avait été assez rapide, il aurait pu esquiver à temps ...

Les e-mails du serveur de test sont plus faciles à ignorer, mais ne préférez-vous pas simplement être averti UNE FOIS que quelque chose ne va pas avec la méthode de test et consulter les journaux de test pertinents vous-même?


3

Si vous pouvez supposer en toute sécurité que faire une demande de serveur avec les mêmes paramètres se comportera toujours de la même manière, la méthode B est presque inutile - pourquoi devriez-vous appeler quatre fois la même méthode pour obtenir les mêmes données de réponse quatre fois lorsqu'un appel suffit?

Et si vous ne pouvez pas assumer cela en toute sécurité et que vous souhaitez l'intégrer au test, il est préférable de lancer le test A plusieurs fois.

La seule situation hypothétique que je vois où B pourrait avoir un avantage est lorsque votre cadre de test autorise uniquement l'activation et la désactivation de méthodes de test explicites, et vous vous attendez à la nécessité de le faire pour les étapes individuelles de votre test.

L'alternative C semble combiner A avec le seul avantage que j'ai mentionné ci-dessus pour B. Si votre cadre de test permet à votre code d'être structuré facilement comme ça, sans trop de frais généraux au-dessus de B, c'est une approche réalisable. Cependant, cela ajoute une complexité supplémentaire à A, donc je ne l'utiliserais que si je veux activer et désactiver les tests individuels, sinon appliquer le principe YAGNI et s'en tenir à la solution la plus simple (A).

TLDR: commencez par A si vous êtes sûr de toujours vouloir exécuter toutes les assertions en un seul test, refactorisez-le en C si vous remarquez que vous avez besoin de contrôler plus facilement de l'extérieur les assertions individuelles.


0

Comme tout code, évitez l'optimisation prématurée. Écrivez d'abord vos tests afin qu'ils soient simples à lire et simples à maintenir. Lorsque les tests commencent à devenir trop lents, optimisez-les. Dans votre exemple assez simple, A et B seront tous deux faciles à lire et à entretenir, alors choisissez celui que vous voulez jusqu'à ce que les choses deviennent trop lentes (structure B) ou trop compliquées (structure A).

Si votre serveur est sans état, vous pouvez optimiser en comparant la réponse réelle avec une réponse attendue pour vérifier l'ensemble du message en une seule fois. Évidemment, cela se fera au détriment de la lisibilité.

Si votre serveur est complet et que vous devez effectuer plusieurs appels API lents pour mettre le serveur dans un état pour le test, vous devez adopter une approche différente ou vos tests peuvent prendre quelques minutes pour s'exécuter. Par exemple, vous pouvez exécuter une mise à jour de la base de données pour injecter des données dans une base de données de test afin d'obtenir rapidement un objet dans un état approprié pour le test. Le test est rapide et lisible mais plus difficile à maintenir. Alternativement, vous pourriez être en mesure d'écrire une façade devant l'api afin que plusieurs appels api deviennent des appels api uniques qui correspondent plus étroitement au processus métier que vous testez.


0

Les tests ne doivent pas partager des choses - en partant de zéro, vous évitez l'influence d'un test sur un autre. Cela vous permet également d'exécuter des tests dans un ordre aléatoire.
La voie C ne doit donc pas être acceptée.


Lorsque vous écrivez du code (ou peut-être même créez autre chose), demandez-vous toujours: "pourquoi existe-t-il une telle pratique?"
Pourquoi nous disons qu'il devrait y avoir différents tests pour tout?

Il y a deux cas où vous en avez besoin:

  1. lorsque vous ne pouvez pas compter sur «chaque étape de test ne peut réussir que si toutes les précédentes ont réussi»
  2. lorsque vos tests n'ont pas de messages d'assertion descriptifs

Il y a deux raisons pour lesquelles vous faites face à ces cas:

  1. "chaque étape de test ne peut réussir que si toutes les précédentes ont réussi" n'est vraiment pas applicable à la fonctionnalité de votre produit
  2. vous n'avez pas suffisamment de connaissances sur le produit en raison du manque d'expérience ou de temps, ou de la complexité écrasante du produit

Si pour une raison quelconque ne peut pas déclarer au moins une de ces raisons d'avoir lieu de prendre aveuglément la structure de B .


Dans le cas contraire (je l' espère vous ici) vous choisissez A .


Vous pouvez également poser cette question sur le site Stackexchange d' assurance qualité et de test de logiciels.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.