Maximilian
Veröffentlicht am
#testen #Typoskript #javascript #Lernprogramm
TLDR
Wenn Ihnen der Kontext dieser Tests egal ist und Sie direkt damit fortfahren möchtenKnotenabruf
spottet, springe zumHier ist meine LösungAbschnitt.
Einführung
Ich habe eine Middleware-Bibliothek für Mikrodienste geschrieben, die JWTs dekodiert und überprüft und mit Express und Koa funktioniert. Die Anforderung bestand darin, dass die Bibliothek eine API-Anfrage an einen externen Dienst stellen musste, um Token zu aktualisieren, wenn das bereitgestellte Token abgelaufen war. Ich schreibe diesen Beitrag nicht, um die Bibliothek selbst zu diskutieren, sondern um darüber zu sprechen, wie ich die Komponententests dafür geschrieben habe, da ich es etwas schwierig fand, eine Lösung zu implementieren, die das Senden und Empfangen dynamischer Daten an und von dem externen Dienst ermöglicht , während die Tests isoliert bleiben. Hoffentlich hilft dies jemandem, der etwas Ähnliches versucht.
Die Middleware
Die Controller-Funktion sieht in etwa so aus:
asynchron Funktion checkToken( reqHeader: EingehendeHttpHeader): Versprechen<ITokenData> { // Zugriffstoken aus Authentifizierungsheader abrufen const Zugangstoken = reqHeader.Genehmigung?.Teilt(/\S+/)[1]; // Token dekodieren const decodedToken = erwarten verifyAndDecodeToken(Zugangstoken, GEHEIMNIS); // Token ist gültig, gebe den dekodierten Token zurück Wenn (decodedToken.exp > Datum.Jetzt() / 1000) zurückkehren decodedToken.tokenData; // Speichere die Daten des dekodierten Tokens in einer Variablen const tokenData: ITokenData = decodeToken.tokenData; // Rufen Sie die externe API mit den aus dem Zugriffstoken dekodierten Daten auf const newAccessToken = erwarten Aktualisierungstokens(tokenData); // Von der externen API zurückgegebenes Dekodier-Token const decodedNewToken = erwarten verifyAndDecodeToken(newAccessToken, GEHEIMNIS); // Den dekodierten neuen Token zurückgeben zurückkehren checkNewToken.tokenData;}
DererfrischendTokens()
Funktion sieht in etwa so aus:
asynchron Funktion Aktualisierungstokens( tokenData: ITokenData): Versprechen<Zeichenfolge | nicht definiert> { const res = erwarten bringen(„https://refreshmytokensyouslag.com“., { Methode: „Beitrag“., Körper: JSON.stringifizieren({ tokenData }), Kopfzeilen: { "Inhaltstyp": `application/json`, }, }); const reJson = erwarten res.json(); zurückkehren reJson?.Daten.newAccessToken;}
Und nur der Übersichtlichkeit halber sehen die Wrapper-Funktionen (oder „Fabriken“) für Koa und Express in etwa so aus:
/** * Middleware-Fabrik für Express */Funktion checkTokenExpress() { zurückkehren asynchron Funktion checkTokenMiddleware( erf: äußern.Anfrage, res: äußern.Antwort, nächste: äußern.NächsteFunktion ): Versprechen<Leere> { const decodedToken = erwarten checkToken(erf.Kopfzeilen); erf.Benutzerdaten = decodedToken; zurückkehren Leere nächste(); };}/** * Middleware-Fabrik für Koa */Funktion checkTokenKoa() { zurückkehren asynchron Funktion checkTokenMiddleware( ctx: So.Kontext, nächste: So.Nächste ): Versprechen<Leere> { const decodedToken = erwarten checkToken(ctx.Kopfzeilen); ctx.Benutzerdaten = decodedToken; erwarten nächste(); };}
Code-Erklärung
Wir haben unsere 2 „Middleware-Fabriken“; eine für Express und eine für Koa. Beide werden exportiert und können in anderen Express- oder Koa-Diensten als Middleware verwendet werden. Beide Fabriken nennen dascheckToken()
Funktion, ein dekodiertes Token anhängenerf
oderctx
Objekte bzw. dann aufrufennächste()
.
Unsere Controller-Funktion,checkToken()
, überprüft und dekodiert Zugriffstoken. Wenn das Token gültig und nicht abgelaufen ist, wird das decodierte Token-Objekt zurückgegeben. Wenn das Token ungültig ist, wird ein Fehler ausgegeben. Wenn das Token gültig, aber abgelaufen ist, wird das aufgerufenerfrischendTokens()
Funktion.
DererfrischendTokens()
Die Funktion stellt eine Anfrage an eine externe API, die die Ausgabe neuer Zugriffstoken übernimmt, wenn bestimmte Bedingungen erfüllt sind. UnsercheckToken()
Die Funktion dekodiert dann dieses neue Token und gibt es zurück.
Die Tests
Das Testen der Gültigkeit des Tokens war ziemlich einfach, da der Code bereits isoliert ist. So sieht der Code für die Koa- und Express-Implementierungen aus:
// Äußernprüfen(„Middleware ruft als nächstes auf, wenn das Zugriffstoken gültig ist“., asynchron () => { // Ein Token zum Testen erstellen const testAccessToken = jwt.Zeichen({ foo: „Bar“. }, GEHEIMNIS, { Läuft ab in: „1h“., }); // Das Anforderungsobjekt verspotten const MockReq = { Kopfzeilen: { Genehmigung: „Träger${testAccessToken}` }, }; // Das Antwortobjekt verspotten const MockRes = {}; const erf = MockReq als Unbekannt als ExpressRequest; const res = MockRes als Unbekannt als ExpressResponse; // Verspotte die Funktion next() const nächste = Ansonsten.Stummel(); // Express aufrufen const Middleware = äußern(GEHEIMNIS); Leere (erwarten Middleware(erf, res, nächste)); // Unsere Testerwartung erwarten(nächste.callCount).sein(1);});// Soprüfen(„Middleware ruft als nächstes auf, wenn das Zugriffstoken gültig ist“., asynchron () => { // Ein Token zum Testen erstellen const testAccessToken = jwt.Zeichen({ foo: „Bar“. }, GEHEIMNIS, { Läuft ab in: „1h“., }); // Das CTX-Objekt verspotten const MockCtx = { Kopfzeilen: { Genehmigung: „Träger${testAccessToken}` }, }; const ctx = MockCtx als Unbekannt als KoaContext; // Verspotte die Funktion next() const nächste = Ansonsten.Stummel(); // Soldat anrufen const Middleware = zu(GEHEIMNIS); Leere (erwarten Middleware(ctx, nächste)); // Unsere Testerwartung erwarten(nächste.callCount).sein(1);});
Code-Erklärung
Die Tests für Express und Koa sind nahezu identisch, wir müssen nur Express berücksichtigen.Anfrage
Objekt und Koasctx
Objekt.
In beiden Tests erstellen wir ein gültiges TokentestAccessToken
und verspotten dienächste()
Funktioniert mit Sinon. Wir verspotten dann dasAnfrage
UndAntwort
Objekte für Express und diectx
Objekt für Koa. Danach rufen wir die Middleware auf und teilen Jest mit, dass wir das erwartennächste()
Die Funktion muss einmal aufgerufen werden, d. h. wir erwarten, dass das Token gültig ist und die Middleware es uns ermöglicht, mit dem nächsten Schritt in unserer Anwendung fortzufahren.
Wie sieht eine Prüfung auf einen Fehler aus?
Von diesem Punkt an gebe ich nur noch Codebeispiele in Koa, da etwas weniger Code durchgelesen werden muss, aber Sie sollten kein Problem damit haben, ihn anhand der obigen Beispiele für Express anzupassen.
prüfen(„Middleware löst einen Fehler aus, wenn das Zugriffstoken ungültig ist“., asynchron () => { const testAccessToken = „abcd1234“.; const MockCtx = { Kopfzeilen: { Genehmigung: „Träger${testAccessToken}` }, }; const ctx = MockCtx als Unbekannt als KoaContext; const nächste = Ansonsten.Stummel(); const Middleware = zu(GEHEIMNIS, API_URIs); erwarten erwarten(Middleware(ctx, nächste)).lehnt ab.toThrowError( /Zugriffstoken ungültig/i );});
Code-Erklärung
Hier erstellen wir einetestAccessToken
Das ist nur eine zufällige Zeichenfolge, die wir an unsere Middleware übergeben. In diesem Fall erwarten wir, dass die Middleware einen Fehler auslöst, der dem regulären Ausdruck entspricht.Zugriffstoken ungültig
. Der Rest der Logik in diesem Test ist derselbe wie im letzten, insofern, als wir uns nur lustig machenctx
Objekt undnächste
Funktion.
Das Knifflige: Testen dynamischer Aufrufe einer externen API
Wir benötigen immer Tests, die isoliert ausgeführt werden können. Dafür gibt es mehrere Gründe, aber der Hauptgrund ist, dass wir nicht daran interessiert sind, etwas zu testen, das nicht Teil unseres Codes ist und daher außerhalb unserer Kontrolle liegt.
Die Frage ist also: Wie können wir dynamisch verschiedene Antworten von einer externen API oder einem externen Dienst testen?
Zuerst verspotten wir dasKnotenabruf
Bibliothek, was bedeutet, dass jeder Code in der von uns getesteten Funktion verwendet wirdKnotenabruf
wird verspottet. Um die Antworten dynamisch zu gestalten, erstellen wir als Nächstes eine Variable, der wir je nach dem, was wir testen, unterschiedliche Werte zuweisen können. Wir werden dann verspottetKnotenabruf
Funktion, um eine Funktion zurückzugeben, die das von Express und Koa bereitgestellte Antwortobjekt verspottet.
Das ist ein bisschen ein voller Mund. Schauen wir uns also einen Code an ...
Hier ist meine Lösung
An der Spitze meines.spec
Datei haben wir Folgendes (in JS, um es leichter lesbar zu machen):
// Die Variable, die wir für verschiedene Tests ändern könnenlassen MockTokenFromAPI;// Die „node-fetch“-Bibliothek verspottenIst.verspotten(„Knotenabruf“., () => { // Die Funktion, die „node-fetch“ zurückgeben soll const Antwort generieren = () => { // Das Antwortobjekt verspotten zurückkehren { json: () => ({ Daten: { newAccessToken: MockTokenFromAPI } }) }; }; // Alles zusammen, Jest! zurückkehren Ist.fn().mockResolvedValue(Antwort generieren());});
Zuerst bringen wir Jest dazu, sich darüber lustig zu machenKnotenabruf
Bibliothek durch Rückgabe einer Funktion. Anschließend veranlassen wir die simulierte Bibliothek, eine weitere aufgerufene Funktion zurückzugebengenerierenResponse()
. Der Zweck vonAntwort generieren
besteht darin, die Antwortobjekte in Express und Koa zu verspotten, sodass ein Objekt mit zurückgegeben wirdjson
Taste. Der Wert vonjson
ist eine Funktion und verspottet somit die.json()
Methode, die schließlich mithilfe unserer die Datenstruktur zurückgibt, die wir von der API erwartenMockTokenFromApi
Variable. Um das Ganze nun dynamisch zu machen, müssen wir in unseren Tests nur noch den Wert dieser Variablen ändern!
Lassen Sie uns das schreiben ...
Schnittstelle IJsonResponse { Daten: { newAccessToken: Zeichenfolge | nicht definiert; };}Schnittstelle IResponse { json: () => IJsonResponse;}lassen MockTokenFromAPI: Zeichenfolge | nicht definiert;Ist.verspotten(„Knotenabruf“., () => { const Antwort generieren = (): IResponse => { zurückkehren { json: (): IJsonResponse => ({ Daten: { newAccessToken: MockTokenFromAPI }, }), }; }; zurückkehren Ist.fn().mockResolvedValue(Antwort generieren());});
Und jetzt erfahren Sie hier, wie wir unsere Middleware mit dynamischen Antworten von einer externen API mithilfe von testen könnenKnotenabruf
Bibliothek:
prüfen(„Middleware löst einen Fehler aus, wenn Aktualisierungstokenfehler auftreten“., asynchron () => { // Erstellen Sie ein abgelaufenes, aber gültiges Zugriffstoken zum Senden const testAccessToken = jwt.Zeichen({ tokenData: { Authentifizierungs-ID: „1234“. } }, GEHEIMNIS, { Läuft ab in: „0“., }); // Legen Sie dynamisch fest, was die externe API/der externe Dienst zurückgeben soll // In diesem Fall ein ungültiges Token MockTokenFromAPI = „abc123“.; const MockCtx = { Kopfzeilen: { Genehmigung: „Träger${testAccessToken}` }, }; const ctx = MockCtx als Unbekannt als KoaContext; const nächste = Ansonsten.Stummel(); const Middleware = zu(GEHEIMNIS, API_URIs); erwarten erwarten(Middleware(ctx, nächste)).lehnt ab.toThrowError( /Token-Fehler aktualisieren/i );});prüfen(„Middleware ruft als nächstes auf, wenn ein Aktualisierungstoken vorhanden und gültig ist“., asynchron () => { // Erstellen Sie ein abgelaufenes, aber gültiges Zugriffstoken zum Senden const testAccessToken = jwt.Zeichen({ tokenData: { Authentifizierungs-ID: „1234“. } }, GEHEIMNIS, { Läuft ab in: „0“., }); // Legen Sie dynamisch fest, was die externe API/der externe Dienst zurückgeben soll // In diesem Fall ein gültiges Token MockTokenFromAPI = jwt.Zeichen({ tokenData: { Authentifizierungs-ID: „1234“. } }, GEHEIMNIS, { Läuft ab in: „1h“., }); const MockCtx = { Kopfzeilen: { Genehmigung: „Träger${testAccessToken}` }, }; const ctx = MockCtx als Unbekannt als KoaContext; const nächste = Ansonsten.Stummel(); const Middleware = zu(GEHEIMNIS, API_URIs); Leere (erwarten Middleware(ctx, nächste)); erwarten(nächste.callCount).sein(1);});
Abschluss
Wir haben jetzt die Möglichkeit, eine 100 % isolierte Testabdeckung für unsere Middleware zu erhalten, auch wenn diese auf einer externen API basiert.
Ich hoffe, das hat Ihnen irgendwie geholfen, und wenn nicht, hoffe ich, dass Sie etwas gelernt haben oder es zumindest interessant fanden!
FAQs
What can I use instead of fetch in node JS? ›
- Axios. Axios is a promise-based HTTP client for Node.js and the browser. ...
- Got. Got is yet another user-friendly and robust HTTP request framework for Node. ...
- SuperAgent. SuperAgent is a tiny HTTP request library that can be used in Node. ...
- Request. Request is among the most popular Node. ...
- WebScrapingAPI.
There are several different ways you can fetch data from an API in Node. js. Some of the most common methods include using the built-in http module, using a third-party library like Axios or the request-promise library, and using the fetch() function or the fetch API.
How to mock response in jest? ›To mock a function's return value in Jest, you first need to import all named exports from a module, then use mockReturnValue on the imported function. You can use the * as <alias> inside an import statement to import all named exports. Then, you need to chain mockReturnValue off of jest.
How to write fetch in node js? ›How to Use Fetch? To use the NodeJS Fetch API, we need to call the fetch() method as we do on the client side. fetch(url[, options]); The url parameter is the URL of the host from which we want to fetch the resource.
What is the alternative to fetch in typescript? ›In the Javascript language, Axios is an alternative to the fetch() method because it can do an automatic analysis of JSON and works very well in partnership with Express.
What is the best way to fetch data in React js? ›- 7 Ways To Fetch Data in React Applications. Data fetching and caching quickly becomes cumbersome and complex without the right tools. ...
- Relay. ...
- Apollo Client. ...
- TanStack Query v4 (react-query) ...
- SWR. ...
- React-async-hook. ...
- Axios. ...
- Fetch API.
Mocking Node modules
There's no need to explicitly call jest.mock('module_name') . Scoped modules (also known as scoped packages) can be mocked by creating a file in a directory structure that matches the name of the scoped module.
In Jest, Node. js modules are automatically mocked in your tests when you place the mock files in a __mocks__ folder that's next to the node_modules folder. For example, if you a file called __mock__/fs. js , then every time the fs module is called in your test, Jest will automatically use the mocks.
How to mock an object using Jest? ›To mock an object in Jest, use the jest. mock() function with the path to the module you want to mock. You can then define a mock implementation for the object's methods and properties using jest. fn().
How do I get data from fetch response? ›The Fetch API allows you to asynchronously request for a resource. Use the fetch() method to return a promise that resolves into a Response object. To get the actual data, you call one of the methods of the Response object e.g., text() or json() . These methods resolve into the actual data.
How to fetch data from json in node js? ›
- Read JSON data from disk.
- Learn to use fs module to interact with the filesystem.
- Persist data to a JSON file.
- Use JSON. parse and JSON. stringify to convert data to and from JSON format.
fetch() global function. The global fetch() method starts the process of fetching a resource from the network, returning a promise which is fulfilled once the response is available. The promise resolves to the Response object representing the response to your request.
How to fetch data from backend to frontend in node js? ›In order to achieve this you will need websocket connections from frontend to server. websockets are a bidirectional connection between your server and client. while the clients are connected to the server via sockets, the server can send events to the frontend without receiving a request first.
What is the difference between cross fetch and fetch? ›What's the difference between them? cross-fetch is a ponyfill module, while isomorphic-fetch is a polyfill module, at least based on a brief glance at their usage at the top of their respective README files. Otherwise, they're two completely different implementations of the exact same fetch API specification.
How to use fetch method in JavaScript? ›The fetch() method starts the process of fetching a resource from a server. The fetch() method returns a Promise that resolves to a Response object. 😀 No need for XMLHttpRequest anymore.
How to use fetch in node js TypeScript? ›- const url: string = 'https://jokes-by-api-ninjas.p.rapidapi.com/v1/jokes'; interface IAPIOptions { method: string; headers: { ...
- } const options: IAPIOptions = { method: 'GET', headers: { ...
- }; async function getJokes() { try { const response = await fetch(url, options);
An example
fetch(`https://user.api.com`, { method: "POST", body: JSON. stringify({ name: "Simon", }), }); This would create a promise, and that promise would have the type Promise<Response> . To access the data that is returned from the API you would want to call .
- Isolate the fetching logic into a service folder.
- Add a layer of abstraction between the API response and the App.
- Return function response as an object.
- Correctly assert the TS typings with Discriminated Union.
For example, your request might fail, your database might be unreachable, or your logging service might have reached its quota. This is why useEffect is the hook for us - by fetching data, we're making our React component impure, and useEffect provides us a safe place to write impure code.
What is the recommended method to fetch data in next JS? ›The Next.js App Router allows you to fetch data directly in your React components by marking the function as async and using await for the Promise . Data fetching is built on top of the fetch() Web API and React Server Components. When using fetch() , requests are automatically deduped by default.
What are the different types of data fetching? ›
Types of data fetching
Generally speaking, in the modern frontend world, we can loosely separate the concept of “data fetching” into two categories: initial data fetching and data fetching on demand. Data on demand is something that you fetch after a user interacts with a page, in order to update their experience.
For your NodeJS applications, Jest framework can be used for Unit Testing.
How to mock an API in Jest? ›- Import the module you want to mock into your test file.
- jest. mock() the module.
- Use . mockResolvedValue(<mocked response>) to mock the response. That's it! Here's what our test looks like after doing this: // index. test.
To spy on an exported function in jest, you need to import all named exports and provide that object to the jest. spyOn function. That would look like this: import * as moduleApi from '@module/api'; // Somewhere in your test case or test suite jest.
How do I mock a whole file in Jest? ›To mock any file first step is to tell Jest that you are planning to mock it. After telling Jest that you will be mocking the particular file, you need to tell Jest what it should do, instead of executing the function. You can increase a counter or return a particular value to know that the function was called.
How to mock local function in Jest? ›You can create a namespace that you export as the default object and call b using the namespace. This way, when you call jest. mock it will replace the b function on the namespace object. const f = require('./f'); jest.
How do you mock an object for testing? ›We can mock an object using @Mock annotation too. It's useful when we want to use the mocked object at multiple places because we avoid calling mock() method multiple times. The code becomes more readable and we can specify mock object name that will be useful in case of errors.
How to mock a package in Jest? ›mock() inside the test; you must call it at the top level of the module. However, you can call mockImplementation() inside individual tests if you want to set up different mocks for different tests. The reason that you must have jest. mock at the top of your tests, is internally jest will reorder the jest.
Why use Axios instead of fetch? ›Axios has built-in XSRF(Cross-Site Request Forgery) protection, while Fetch does not. Axios has the ability to intercept HTTP requests but Fetch, by default, does not. Axios allows canceling requests and request timeout but fetch does not.
Can I require node-fetch? ›You can require("node-fetch-commonjs") directly. You will not see the. It works on older Node. js versions that don't support requiring built-in modules with a node: prefix.
What is the difference between node node-fetch and Axios? ›
Different properties are used for a post request to send data to the endpoint - Axios uses the data property, whereas with fetch we use the body property. We need to serialize data into a JSON string to send data. Axios automatically stringifies data when sending JavaScript objects to the API using the POST method.
What is the difference between node fetch and axios typescript? ›To send data, fetch() uses the body property for a post request to send data to the endpoint, while Axios uses the data property. The data in fetch() is transformed to a string using the JSON.stringify method.
Why is fetch better than Ajax? ›Fetch is a newer API that is part of the JavaScript language and is used to make asynchronous requests to a server. It is a more modern approach to making requests and is more efficient than Ajax in jQuery. Additionally, Fetch is more secure than Ajax in jQuery, as it does not expose the underlying code to the server.
Why is Fetch API better? ›The Fetch API allows you to make network requests similar to XMLHttpRequest (XHR). The main difference is that the Fetch API uses Promises, which enables a simpler and cleaner API, avoiding callback hell and having to remember the complex API of XMLHttpRequest.
What are the disadvantages of Fetch API? ›- not supported in IE, there is a polyfill, but it can implement only a subset of features.
- Previously it didn't have ability to abort requests. Now it's possible with a help of "AbortController+AbortSignal" API.
- Does not support tracking progress. ...
- Does not support requests timeouts.
In all popular browsers, the Fetch API has native support. The node-fetch package is used by JavaScript developers to create server-side code.
Can I use fetch without await? ›Without async/await
Fetch sends the Request and returns a promise, which is resolved to the Response object when the request completes. If the request fails, the promise is rejected. To get the data received in the response, you need to wait for this promise to resolve into the Response object.
APIs are broadly accepted and used in web applications. There are four different types of APIs commonly used in web services: public, partner, private and composite.
Is fetch a RESTful API? ›Fetch API allows us to get data from a local or remote server via a RESTful API. It is a promise-based asynchronous API where we can either await it or use promise chaining to get the response. This is where XMLHttpRequest and the Fetch API differ.
How many ways can you fetch API? ›There are different of fetching data: By using Fetch API. By using Axios library. By using async-await syntax.
What is the difference between node-fetch and isomorphic-fetch? ›
FETCH is polyfill for browsers which don't have fetch function (caniuse.com/#search=fetch). It will add fetch function to your browser window object. While isomorphic-fetch is implementation of fetch for both node. js and browser, built on top of fetch polyfill.
Does node-fetch work in the browser? ›Introduction to Node-Fetch API
While long ago, HTTP Requests were done using XMLHttpRequest or XHR Objects, nowadays all modern browsers support the Fetch API from javascript.
fetch() allows you to make network requests similar to XMLHttpRequest (XHR). The main difference is that the Fetch API uses Promises, which enables a simpler and cleaner API, avoiding callback hell and having to remember the complex API of XMLHttpRequest.