Les tests fictifs HTTP vous permettent d'implémenter et de tester une fonctionnalité même si les services dépendants ne sont pas encore implémentés. En outre, vous pouvez tester des services déjà mis en œuvre sans appeler réellement ces services. En d'autres termes, vous pouvez les tester hors ligne. Vous devez d'abord analyser vos besoins métier pour cette application, puis les écrire sous forme de scénarios dans vos documents de conception..
Supposons que vous développiez une API cliente Node.js pour un client multimédia qui est un service (il n’existe pas de tel service dans la vie réelle, uniquement pour des tests) pour la musique, les vidéos, les images, etc. cette API, et il pourrait y avoir un besoin de changements de temps en temps.
Si vous n'avez aucun test pour cette API, vous ne saurez pas quels problèmes cela va causer. Toutefois, si vous avez des tests pour cette API, vous pouvez détecter les problèmes en exécutant tous les tests que vous avez écrits auparavant. Si vous développez une nouvelle fonctionnalité, vous devez ajouter des scénarios de test spécifiques. Vous pouvez trouver une API qui a déjà été implémentée dans ce référentiel GitHub. Vous pouvez télécharger le projet et lancer test npm
afin d'exécuter tous les tests. Continuons la partie test.
Pour un test de simulation HTTP, nous utiliserons nock, qui est une bibliothèque d'attentes et de requêtes HTTP pour Node.js. Dans le test fictif HTTP, vous pouvez appliquer le flux suivant:
Pensons à une API de client multimédia. Disons que vous appelez la fonction musiquesListe
en utilisant une instance de Médias
bibliothèque comme ci-dessous:
var Media = require ('… / lib / media'); var mediaClient = new Media ("your_token_here"); mediaClient.musicsList (fonction (erreur, réponse) console.logs (réponse);)
Dans ce cas, vous aurez une liste de musique dans le réponse
variable en faisant une demande à https://ap.example.com/musics
à l'intérieur de cette fonction. Si vous voulez écrire pour cela, vous devez simuler des requêtes afin que vos tests soient exécutés hors ligne. Simulons cette requête en bloc.
describe ('Tests de musique', function () it ('devrait lister la musique', function (done) nock ('https://api.example.com') .get ('/ musics') .reply (200 , 'OK'); mediaClient.musicList (fonction (erreur, réponse) expect (réponse) .to.eql ('OK') done ())))
décris ('Tests de musiques', fonction ()…
est pour grouper vos tests. Dans l'exemple ci-dessus, nous regroupons les tests relatifs à la musique dans le même groupe.. il ('devrait lister la musique', fonction (done)…
est destiné à tester des actions spécifiques dans des fonctions liées à la musique. Dans chaque test, le terminé
Le rappel est fourni afin de vérifier le résultat du test dans la fonction de rappel de la fonction réelle. Dans la demande fictive, nous supposons qu'il répondra D'accord
si on appelle le musicList
une fonction. Le résultat attendu et réel est comparé dans la fonction de rappel..
Vous pouvez définir vos données de demande et de réponse dans un fichier. Vous pouvez voir l'exemple ci-dessous pour faire correspondre les données de réponse d'un fichier.
it ('devrait créer de la musique et répondre comme dans le fichier de ressources', function (done) nock ('https://api.example.com') .post ('/ musics', titre: 'De la fumée sur l'eau' , auteur: 'Deep Purple', durée: '5,40 min.') .reply (200, function (uri, requestBody) return fs.createReadStream (chemin.normalize (__ nom de répertoire + '/resources/new_music_response.json'), ' utf8 '))); mediaClient.musicCreate (titre:' Fumée sur l'eau ', auteur:' Deep Purple ', durée:' 5,40 min. ', fonction (erreur, réponse) expect (JSON.parse ( réponse) .music.id) .to.eql (3164495) done ()))
Lorsque vous créez un morceau de musique, la réponse doit correspondre à la réponse du fichier..
Vous pouvez également chaîner une demande fictive dans un domaine comme ci-dessous:
it ('il devrait créer de la musique puis supprimer', function (done) nock ('https://api.example.com') .post ('/ musics', titre: 'Maps', auteur: 'Maroon5 ', durée:' 5:00 min. ') .reply (200, music: id: 3164494, titre:' Maps ', auteur:' Maroon5 ', durée:' 7:00 min. ') .delete ('/ musics /' + 3164494) .reply (200, 'Musique supprimée') mediaClient.musicCreate (titre: 'Maps', auteur: 'Maroon5', durée: '5:00 min.', fonction (erreur, réponse) var musicId = JSON.parse (réponse) .music.id s’attend (musicId) .to.eql (3164494) mediaClient.musicDelete (musicId, fonction (erreur, réponse) expect (réponse) .to. eql ('Musique supprimée') done ())))
Comme vous pouvez le constater, vous pouvez commencer par simuler la création et la réponse de la musique, puis la suppression de la musique et enfin la réponse à d’autres données..
Dans le client média, Type de contenu: application / json
est fourni dans les en-têtes de demande. Vous pouvez tester un en-tête spécifique comme ceci:
it ('devrait fournir un jeton dans l'en-tête', function (done) nock ('https://api.example.com', reqheaders: 'Content-Type': 'application / json') .get ( '/ musics') .reply (200, 'OK') mediaClient.musicList (function (error, response) expect (réponse) .to.eql ('OK') done ()))
Lorsque vous faites une demande à https://api.example.com/musics
avec un en-tête Type de contenu: application / json
, il sera intercepté par le faux mock HTTP ci-dessus, et vous pourrez tester le résultat attendu et réel. Vous pouvez également tester les en-têtes de réponse de la même manière en indiquant simplement l'en-tête de réponse dans la section Reply:
it ('devrait fournir un en-tête spécifique en réponse', function (done) nock ('https://api.example.com') .get ('/ musics') .reply (200, 'OK', 'Content -Type ':' application / json ') mediaClient.musicList (function (erreur, réponse) expect (réponse) .to.eql (' OK ') done ()))
Vous pouvez spécifier des en-têtes de réponse par défaut pour les demandes dans une étendue en utilisant defaultReplyHeaders
comme suit:
it ('devrait fournir l'en-tête de réponse par défaut', function (done) nock ('https://api.example.com') .defaultReplyHeaders ('Content-Type': 'application / json') .get (' / musics ') .reply (200,' OK, avec en-têtes de réponse par défaut ') mediaClient.musicList (fonction (erreur, réponse) expect (réponse) .to.eql (' OK, avec en-têtes de réponse par défaut ') done () ))
Lorsque vous appelez l'API, vous trouverez les en-têtes de réponse par défaut dans la réponse, même si cela n'est pas spécifié..
Les opérations HTTP sont les bases des demandes des clients. Vous pouvez intercepter toute opération HTTP en utilisant intercepter
:
scope ('http://api.example.com') .intercept ('/ musics / 1', 'DELETE') .reply (404);
Lorsque vous essayez de supprimer de la musique avec l'identifiant 1, il répondra 404. Vous pouvez comparer votre résultat actuel à 404 pour vérifier l'état du test. Aussi, vous pouvez utiliser OBTENIR
, POSTER
, EFFACER
, PIÈCE
, METTRE
, et TÊTE
de la même manière.
Normalement, les requêtes fictives faites avec nock sont disponibles pour la première fois. Vous pouvez le rendre disponible autant que vous le souhaitez, comme ceci:
var scope = nock ('https://api.example.com') .get ('/ musics') .times (2) .reply (200, 'OK avec liste de musique'); http.get ('https://api.example.com/musics'); // "OK avec liste de musique" http.get ('https://apiexample.com/musics'); // "OK avec liste de musique" http.get ('https://apiexample.com/musics'); // "Réelle réponse de api"
Vous êtes limité à faire deux demandes en tant que demandes HTTP simulées. Lorsque vous faites trois demandes ou plus, vous ferez automatiquement une demande réelle à l'API..
Vous pouvez également fournir un port personnalisé dans l'URL de votre API:
it ('doit gérer un port spécifique', function (done) nock ('https://api.example.com:8081') .get ('/') .reply (200, 'OK avec le port personnalisé') ('https://api.example.com:8081', fonction (erreur, réponse, corps) attend (corps) .to.eql ('OK avec port personnalisé') done ()))
Le filtrage d'étendue devient important lorsque vous souhaitez filtrer le domaine, le protocole et le port pour vos tests. Par exemple, le test ci-dessous vous permettra de simuler des demandes comportant des sous-domaines tels que API0, API1, API2, etc..
it ('devrait aussi supporter les sous-domaines', function (done) nock ('http://api.example.com', filteringScope: function (scope) return / ^ http: \ / \ / api [0- 9] *. Example.com/.test(scope);) .get ('/ musics') .reply (200, 'OK avec les sous-domaines dynamiques') demande ('http://api2.example.com/ musiques ', fonction (erreur, réponse, corps) expect (body) .to.eql (' OK avec les sous-domaines dynamiques ') done ()))
Vous n'êtes pas limité à une seule URL ici; vous pouvez tester les sous-domaines spécifiés dans le expression rationnelle
.
Parfois, vos paramètres d'URL peuvent varier. Par exemple, les paramètres de pagination ne sont pas statiques. Dans ce cas, vous pouvez utiliser le test suivant:
it ('devrait supporter la pagination dynamique', function (done) nock ('http://api.example.com' ') .filteringPath (/ page = [^ &] * / g,' page = 123 ') .get ('/ musics? page = 123') .reply (200, requête 'Ok avec pagination') ('http://api.example.com/musics?page=13', fonction (erreur, réponse, corps) expect (body) .to.eql ('Ok réponse avec paginate') done ()))
Si vous avez différents champs dans le corps de votre demande, vous pouvez utiliser filteringRequestBody
pour éliminer divers champs comme celui-ci:
it ('devrait créer un film avec un titre dynamique', function (done) nock ('http://api.example.com' ') .filteringRequestBody (function (path) return' test ') .post (' / musics ',' test ') .reply (201,' OK '); var options = url:' http://api.example.com/musics ', méthode:' POST ', corps:' author = test_author & title = test ' request (options, fonction (err, réponse, corps) expect (body) .to.eql (' OK ') done ()))
Ici auteur
peut varier, mais vous pouvez intercepter le corps de la demande avec titre = test
.
Dans ce client, un jeton est utilisé pour authentifier l'utilisateur client. Vous devez vérifier l’en-tête pour voir un jeton valide dans le Autorisation
entête:
it ('doit correspondre à l'en-tête du jeton du porteur', function (done) nock ('https://api.example.com') .matchHeader ('Authorization', /Bearer.*/) .get ('/ musics') .reply (200, 'Ok réponse avec liste de musique') mediaClient.musicList (fonction (erreur, réponse) expect (réponse) .to.eql ('Ok réponse avec liste de musique') done ()))
Dans les cas de test, vous pouvez activer des requêtes HTTP réelles en définissant allowUnmocked
comme vrai. Regardons le cas suivant:
it ('demande exécutée sur "http://api.example.com"', fonction (done) var scope = nock ('http://api.example.com', allowUnmocked: true) .get ('/ musics') .reply (200, 'OK'); http.get ('http://api.example.com/musics'); // Ceci sera intercepté par le nock http.get ('http: //api.example.com/videos '); // Ceci fera une vraie demande de service)
Dans un scénario Nock, vous pouvez voir un scénario pour le / musiques
URI, mais aussi si vous faites une URL différente de / musiques
, ce sera une demande réelle à l'URL spécifiée au lieu d'un test qui échoue.
Nous avons expliqué comment rédiger des scénarios en fournissant un exemple de demande, une réponse, un en-tête, etc., et en vérifiant les résultats réels et attendus. Vous pouvez également utiliser des utilitaires d’attente tels que est fait()
, effacer le scénario de la portée avec Nettoie tout()
, utiliser un scénario Nock pour toujours en utilisant persister()
, et lister les mocks en attente à l'intérieur de votre cas de test en utilisant waitingMocks ()
.
Supposons que vous ayez écrit un scénario nul et que vous exécutiez une fonction réelle dans un scénario test. Dans votre scénario de test, vous pouvez vérifier si le scénario écrit est exécuté ou non comme suit:
it ('demande exécutée sur "http://api.example.com" ", fonction (done) var musicList = nock (' http://api.example.com ') .get (' / musics ') .reply (200, 'OK avec liste de musique'); request ('http://api.example.com/musics', fonction (erreur, réponse) expect (musicList.isDone ()). to to.eql (true ) terminé() ) )
À l'intérieur du rappel de demande, nous nous attendons à ce que cette URL du scénario nock (http://api.example.com/musics) soit appelée..
Si vous utilisez un scénario Nock avec persister()
, il sera disponible pour toujours pendant l'exécution de votre test. Vous pouvez effacer le scénario à tout moment en utilisant cette commande, comme indiqué dans l'exemple suivant:
it ('devrait échouer à cause de l'effacement de la portée', function (done) var musicList = nock ('http://api.example.com' ') .get (' / musics ') .reply (200,' OK avec la musique list '); nock.cleanAll (); request (' http://api.example.com/musics ', fonction (erreur, réponse, corps) attend (corps) .not.eql (' OK avec liste de musique ' ) terminé() ) )
Dans ce test, notre demande et notre réponse correspondent au scénario. Cependant, après nock.cleanAll ()
nous réinitialisons le scénario et nous n'attendons pas de résultat avec 'OK avec la liste de musique'
.
Chaque scénario Nock est disponible uniquement la première fois. Si vous voulez le faire vivre pour toujours, vous pouvez utiliser un test comme celui-ci:
it ('devrait être disponible pour une requête infinie', function (done) nock ('http://api.example.com') .get ('/ musics') .reply (200, 'OK') // First demande d'appel ('https://api.example.com/musics', fonction (erreur, réponse, corps) expect (body) .to.eql ('OK') done ()) // Deuxième demande d'appel ( 'https://api.example.com/musics', fonction (erreur, réponse, corps) expect (body) .to.eql ('OK') done () (//…)
Vous pouvez répertorier les scénarios de blocage en attente qui ne sont pas encore terminés dans votre test, comme suit:
it ('devrait enregistrer les mockings en attente', function (done) var scope = nock ('http://api.example.com') .persist () .get ('/') .reply (200, 'OK' if (! scope.isDone ()) console.error ('En attente de la réplique:% j', scope.pendingMocks ());
Vous voudrez peut-être aussi voir les actions effectuées pendant l'exécution du test. Vous pouvez y arriver en utilisant l'exemple suivant:
it ('devrait enregistrer toute la requête', function (done) var musicList = nock ('http://api.example.com' ') .log (console.log) .get (' / musics ') .reply ( 200, 'OK avec liste de musique'); demande ('http://api.example.com/musics', fonction (erreur, réponse, corps) attend (corps) .eql ('OK avec liste de musique') done ()))
Ce test générera une sortie comme indiqué ci-dessous:
Faire correspondre les http://api.example.com:80/musics avec GET à http://api.example.com:80/musics: true
Parfois, vous devez désactiver le moquage pour des URL spécifiques. Par exemple, vous pouvez appeler le vrai http://tutsplus.com
, au lieu de celui qui se moque. Vous pouvez utiliser:
nock.nock.enableNetConnect ('tutsplus.com');
Cela fera une demande réelle dans vos tests. En outre, vous pouvez activer / désactiver globalement en utilisant:
nock.enableNetConnect (); nock.disableNetConnect ();
Notez que si vous désactivez Net Connect et essayez d’accéder à une URL non modifiée, vous obtiendrez un NetConnectNotAllowedError
exception dans votre test.
Il est recommandé d’écrire vos tests avant une implémentation réelle, même si votre service dépendant n’est pas encore implémenté. Au-delà, vous devez pouvoir exécuter vos tests avec succès lorsque vous êtes hors ligne. Nock vous permet de simuler votre demande et votre réponse afin de tester votre application..