Comment testez-vous une fonction dont le seul but est d'interroger une API externe, mais l'API utilise une syntaxe de requête complexe?

16

La seule vraie logique réside dans la syntaxe de requête de l'API externe. Je ne veux pas tester s'il interroge l'API, je veux tester qu'il l'interroge de manière à ce que les données correctes soient renvoyées. Par exemple, certains pseudo-code:

function retrieve_related_data(id)
{
  query = "[potentially long, syntactically complex query that
            uses param id to get some data]";
  results = api_wrapper.query(query);
  return results;
}

Un exemple plus concret avec une API composée:

function retrieveLifeSupportingObjectsWithinRegion(id)
{
  query = "
    within region(" + id + ") as r
    find objects matching hydration>0 and temp_range has 75
    send name, id, relative(position, r)        
  ";
  results = astronomicalObjectApiWrapper.query(query);
  return results;
}

The query is in a syntax custom to the API and is complex and there are multiple ways to achieve the same or similar results. The purpose of the function is not to get data identified by id but to find a subset of other data based on a fuzzy relationship to the data identified by id that also meets a few other requirements. The other requirements are always the same regardless of id but may change over time as the system is modified. For example, if the example api added support for gravity information, we may want to change the query to also use gravity to refine the results. Or maybe we come up with a more efficient way to check the temp range, but it doesn't change the results.

Ce que je veux tester, c'est que pour une entrée donnée, idl'ensemble de données correct est renvoyé. Je veux tester cela afin que si quelqu'un gâche la requête de telle sorte qu'elle ne renvoie plus les données correctes en fonction de idcela, elle échouera, mais je veux également que les gens puissent modifier la requête pour l'affiner sans avoir besoin de la modifier également. le test.

Options que j'ai envisagées:

  1. Je pourrais bloquer l'api, mais ce serait soit trop simple (vérifiez que le idest présent dans la requête, puis retournez un ensemble de données attendu s'il l'est ou un ensemble inattendu sinon), trop fragile (vérifiez que la chaîne de requête est exactement ce qui se trouve dans la fonction), ou trop complexe (vérifiez que la requête utilisée est syntaxiquement correcte et entraînera le retour des données correctes).

  2. Je pourrais soumettre la requête à la vraie API, mais les résultats attendus pourraient changer au fil du temps à mesure que les données du système externe changent, en dehors du contrôle du système de test.

  3. Je pourrais envisager de configurer une installation de test de la véritable API afin de contrôler les données dont elle dispose, mais cela demande beaucoup d'efforts.

Je me penche vers le n ° 2 et en fais plus un test d'intégration qui n'est pas souvent exécuté et je vois à quelle fréquence les changements dans les données du système externe provoquent la rupture du test. Je pense que ce serait plus simple pour l'instant, mais je me demande s'il existe des alternatives auxquelles je ne pense pas ou de meilleures façons de résoudre ce problème. Tout avis sera le bienvenu.

Joshua Coady
la source
Je pensais à cela comme un test unitaire, mais c'est peut-être vraiment un test d'intégration ou un test d'acceptation de bas niveau?
Joshua Coady
2
S'agit-il d'une API en lecture seule? Ou, pouvez-vous écrire des données sur lesquelles vous pouvez vérifier vos lectures de manière fiable ?
svidgen
Cette question est-elle différente de "comment tester que mon sql (= syntaxe complexe) retourne les données corrigées"? Avec les bases de données, vous avez généralement quelques tests d'intégration qui testent la syntaxe crud-sql et une fade de référentiel simulée pour vérifier businesslogic
k3b

Réponses:

7

Il peut sembler qu'en validant la réponse de l'API externe, nous testerions notre fonction, mais ce ne serait pas totalement vrai. D'une manière ou d'une autre, nous testerions l'API externe et l'environnement dans lequel l'API s'exécute.

Nos tests doivent être adressés pour garantir le comportement attendu du code que nous avons écrit, pas celui écrit par des tiers.

Dans une certaine mesure, nous devons faire confiance au bon fonctionnement des API et des bibliothèques sur lesquelles nous nous appuyons. Par exemple, nous ne testons généralement pas les composants de framework que nous implémentons.

Pourquoi est-ce que je le dis?

Ce que je veux tester, c'est que pour un identifiant d'entrée donné, l'ensemble de données correct est renvoyé

Qu'est-ce qui serait testé ici? Comme vous l'avez dit, les données et leur exactitude ne sont pas sous notre contrôle, donc nous limiterions le succès de la phase de test à un agent externe que nous n'avons aucun contrôle. Ces tests sont susceptibles de devenir non déterministes et définitivement, nous ne voulons pas de ce type de tests dans notre pipeline de bâtiments .

Une autre préoccupation est de valider le contrat. Je trouverais très utile un contrat teste 1 pour s'assurer que l'intégration fonctionne toujours comme prévu, avant toute version ou déploiement.

Je veux tester cela afin que si quelqu'un gâche la requête de telle sorte qu'elle ne renvoie plus les données correctes en fonction de l'identifiant, elle échouera

Que faire si la requête est correcte, mais que les données sont incorrectes en raison de bogues dans l'API? Non seulement les données sont hors de notre contrôle. La logique l'est aussi.

L'implémentation de tests fonctionnels ou de test de bout en bout peut aider ici. Vous pouvez traiter ces tests pour valider certains chemins d'exécution de sorte que si les API retournent des données incorrectes, cela provoquera probablement des comportements et des sorties inattendus. D'un autre côté, je m'attendrais à ce que l'API génère des erreurs si mes requêtes étaient mal formatées.

Mais je veux aussi que les gens puissent modifier la requête pour l'affiner sans avoir besoin de modifier également le test.

Je suggère de mettre en œuvre un outil à cette fin. Cela pourrait être aussi simple que:

  • Une classe qui s'exécute comme un test mais qui n'appartient pas au plan de test
  • Un script shell + curl

Ou quelque chose de plus sophistiqué. Par exemple, un client autonome.

En tout cas, la fonction sous la question vaut bien deux types de tests:

  • Test de l'unité. Comme vous l'avez dit, vous devez bloquer l'API externe, mais c'est tout le but des tests unitaires. Tester notre code en isolant les dépendances.

  • Test d'intégration. Vérifiez que le code envoie non seulement la demande correcte mais qu'il gère correctement le contenu des réponses, les erreurs, les redirections, etc. Faites des tests pour tous ces cas, mais ne testez pas les données .

Note latérale: Votre question est similaire à - comment testez-vous les instructions SQL de l'application -?

Questions connexes :


1: Vous pourriez être intéressé par la réponse de @ DocBrown à ce sujet

Laiv
la source
"Le problème (IMO) est que vous êtes trop concentré sur le test de l'API externe." - Je ne vois rien qui indique que le demandeur souhaite tester l'API externe. En outre, vous dites "stub the API externe", mais avez-vous des suggestions quant à savoir si le demandeur doit utiliser l'option "trop ​​simple", l'option "trop ​​fragile", l'option "trop ​​complexe" ou une quatrième option?
Tanner Swett
La question OP demande comment tester une fonction qui appelle une API externe. Mais à la lecture de ses doutes, il me semble qu'il met trop l'accent sur le test de la requête et ses résultats. J'ai fait 4 suggestions: (1) ne faites pas de test de l'API. (2) n'utilisez pas les tests d'intégration comme plan de travail pour optimiser la requête. Faites plutôt un outil. (3) retour à la question principale, faire son test unitaire et d'intégration. Mais pas valider le contenu de la réponse de l'API. (4) Demandez au chef de projet s'il doit / peut faire une suite de tests de l'API externe dans le cadre du plan de test du projet.
Laiv
1
Je considère la requête elle-même comme du "code écrit par nous". L'objectif final est des tests automatisés pour nous alerter si nous introduisons un bogue dans notre code. En reprenant ce que vous avez dit à propos des instructions SQL, je suppose que c'est similaire à cela - je suppose que ma question est de savoir comment tester que votre code interroge une API externe de manière à ce que l'API réponde comme prévu (en supposant une réponse nominale). Je pense que ce que vous dites est de laisser cela en dehors des tests unitaires et d'intégration, mais si les requêtes sont essentielles à la mission, nous pourrions configurer séparément d'autres tests automatisés pour tester l'api externe en direct.
Joshua Coady
1
IMO, la meilleure façon de faire un test fonctionnel, le changement de la clause where qui ne sera jamais vrai, provoquera un comportement différent dans un ou plusieurs de mes tests fonctionnels. Les UT ne sont qu'une petite partie du plan de test.
Laiv
2
Merci d'être une caisse de résonance. Je suppose qu'en fin de compte, même si la requête contient une logique personnalisée, elle est en dehors du domaine des tests unitaires car la requête est du code qui est "exécuté" en dehors du système testé. J'avais juste besoin que quelqu'un me le dise plusieurs fois de différentes manières avant de le voir;)
Joshua Coady
2

J'ai vu des vérifications d'unité qui vérifient que la chaîne de requête générée correspond à une valeur attendue.

Pourtant. C'était à mon avis en cas d'utilisation limitée. La syntaxe de la requête était compliquée, peut-être boguée, donc A il y avait une infinité de possibilités de vérification et B même si la chaîne était 'correctement' générée, des résultats inattendus pouvaient être retournés dans l'environnement en direct.

Je pense que vous avez raison d'aller pour votre option 2. exécuter des tests d'intégration contre l'instance en direct.

Tant qu'ils ne sont pas destructifs, ce sont les premiers tests que vous devez écrire, car ils détectent, sans identifier la cause, toute erreur.

L'option 3 «déployer une instance de test avec des données factices» est supérieure. Mais cela n'affecte pas votre écriture de test, car vous pouvez pointer les mêmes tests vers le serveur de test si et quand cela devient une bonne utilisation du temps pour en déployer un.

Ewan
la source
0

Cela dépend de l'API, mais si possible, optez pour l'option # 3 (instance de test privée).

Stubbing l'API (option # 1) est la pire option, pour les raisons que vous avez mentionnées, et emprunter cette voie fera probablement plus de mal que de bien (beaucoup de temps perdu).

L'exécution avec la véritable API (option n ° 2) rend les tests floconneux et peu fiables, et après quelques faux positifs, les gens cesseront de les utiliser. Non seulement les données peuvent changer, mais le service peut également être en panne. À mon avis, cela revient à n'avoir aucun test pour les requêtes et à s'appuyer sur des tests d'intégration / système pour trouver les problèmes. Cela dit, si les données de l'API changent rarement et que l'API elle-même est presque toujours active, cela pourrait être une option viable. La plupart des API ne correspondent pas à cette description.

Finalement, cela revient à l'importance et à la complexité de ces requêtes: s'il y en a plus d'une poignée, et certaines sont suffisamment complexes pour que vous ressentiez le besoin de les tester, j'investirais l'effort de configurer une instance privée pour les tests . Il se paiera tout comme les autres tests unitaires.

Michal Tenenberg
la source
Donc, fondamentalement, vous dites que les tests unitaires (# 1) et les tests d'intégration (# 2) sont nuisibles? Alors que # 3 peut sembler le meilleur d'entre eux, il pourrait aussi être le plus cher. Elle doit être maintenue à chaque fois que l'API change. Sans # 2, vous ne serez pas au courant des éventuels bugs-changements dans la véritable API jusqu'à ce que votre application soit en production (trop tard pour prendre des mesures). Ok, # 1 semble inégalé car il n'y a pas de lignes de code à tester ... aujourd'hui ... Demain, comment sait ...
Laiv
Je dis que les mauvais tests sont nocifs, définitivement. Les tests feuilletés gaspillent beaucoup de temps et d'efforts et font perdre la confiance aux tests unitaires dans leur ensemble. Les tests qui rompent avec les changements d'implémentation (# 1) ou tout simplement au hasard lorsque les changements de données (# 2) ne sont pas de bons tests.
Michal Tenenberg
Le test d'intégration ne teste pas les données. C'est ça. Ils ne peuvent pas casser le test, il suffit de valider l'intégration. Les tests ne sont pas une question de foi, c'est d'avoir de bonnes habitudes qui ajoutent de la valeur à l'application
Laiv