slogan3

Bouw een unit test strategie voor asynchrone code

description

Met deze prompt ontwikkel je een gerichte aanpak voor het testen van asynchrone code, wat vaak complex en foutgevoelig is. Je leert hoe je betrouwbare tests schrijft die async gedrag correct afhandelen, waardoor je minder last hebt van flakkerende tests en bugs die moeilijk te detecteren zijn. Dit verbetert de kwaliteit en stabiliteit van je codebase aanzienlijk.

prompt

Help me een strategie te ontwikkelen voor het schrijven van unit tests voor asynchrone code in mijn project. Mijn programmeertaal: <voer je programmeertaal in>. Voorbeelden van asynchrone functies of methoden: ...

try_prompt

generate_helper
disclaimerOnPageApi

ai_answers

provider: openai
model: gpt-4.1-nano
Natuurlijk! Hier is een strategie voor het schrijven van effectieve unit tests voor asynchrone JavaScript-code: 1. Begrijp het asynchrone gedrag: - Promises: Zorg dat je weet wanneer en hoe je promises worden geresolved of afgewezen. - async/await: Wees je ervan bewust dat functies met async altijd een promise retourneren. - Callbacks: Hoewel minder modern, moet je ook callback-gebaseerde asynchrone code correct testen. 2. Gebruik de juiste testtools: - Jest: Ondersteunt native async/await, promises, en heeft ingebouwde functies zoals `done()` en `mock`-functionaliteit. - Mocha + Chai: Combineer met `done()` callback of return promises voor asynchrone tests. - Sinon: Voor het mocken en stubben van fetch of andere asynchrone functies. 3. Teststrategieën: - Return promises: In testen, zorg dat je promises teruggeeft, zodat de test pas voltooit wanneer de promise is afgehandeld. ```js test('fetchData zou data moeten ophalen', () => { return fetchData().then(data => { expect(data).toBeDefined(); }); }); ``` - Gebruik async/await in tests: Dit maakt je tests overzichtelijker. ```js test('fetchData haalt data op', async () => { const data = await fetchData(); expect(data).toBeDefined(); }); ``` - Mock externe calls: Gebruik mocking libraries zoals Jest mocks of Sinon stubs om daadwerkelijke netwerkverzoeken te vervangen, zodat tests snel en betrouwbaar blijven. ```js jest.spyOn(global, 'fetch').mockResolvedValue({ json: () => Promise.resolve({ key: 'value' }), }); ``` 4. Om flakiness te voorkomen: - Mock altijd externe afhankelijkheden zoals netwerkverzoeken. - Gebruik vaste data en timeouts. - Vermijd afhankelijkheid van externe systemen die kunnen variëren. - Zet goede timeouts en retries indien nodig. - Test niet te langlopende asynchrone processen zonder controle. 5. Best practices: - Schrijf kleine, gerichte tests voor elke asynchrone functie. - Test zowel successcenario’s als foutscenario’s (bijv. rejected promises). - Gebruik setup en teardown functies om mocks en spies netjes te resetten. - Documenteer verwachtingen en gebruik descriptive testnamen. Door deze aanpak te volgen, ontwikkel je betrouwbare en onderhoudbare unit tests voor je asynchrone JavaScript-code.
provider: deepseek
model: deepseek-chat
Absoluut! Hier is een gestructureerde strategie voor het schrijven van robuuste unit tests voor asynchrone JavaScript-code. ### 1. Kernconcept: Hoe test je asynchrone code? Het fundamentele principe is dat je testrunner (bijv. Jest) moet weten wanneer je asynchrone test klaar is. Er zijn drie primaire patronen: * **Async/Await (Aanbevolen):** De modernste en meest leesbare aanpak. * **Promises:** Gebruik `.then()` en `.catch()` in combinatie met `return`. * **Callbacks (Oud, maar moet worden ondersteund):** Gebruik een `done` callback. --- ### 2. Praktische Voorbeelden per Patroon Stel je hebt deze module (`api.js`): ```javascript // api.js async function fetchData(url) { const response = await fetch(url); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return await response.json(); } module.exports = { fetchData }; ``` #### Testen met **Async/Await** (Jest & Vitest): ```javascript // api.test.js const { fetchData } = require('./api'); // Mock de globale fetch *voordat* je tests worden geïmporteerd/gedefinieerd global.fetch = jest.fn(); // Of vitest.fn() in Vitest describe('fetchData', () => { beforeEach(() => { // Maak de mock voor elke test schoon fetch.mockClear(); }); it('should return data when fetch is successful', async () => { // Let op de `async` keyword // 1. ARRANGE: Mock een succesvolle response const mockData = { title: 'Test Post' }; fetch.mockResolvedValueOnce({ ok: true, json: async () => mockData, // json is zelf ook async }); // 2. ACT: Roep de async functie aan met `await` const result = await fetchData('https://api.example.com/data'); // 3. ASSERT: Controleer het resultaat en of fetch correct is aangeroepen expect(result).toEqual(mockData); expect(fetch).toHaveBeenCalledWith('https://api.example.com/data'); }); it('should throw an error when fetch fails', async () => { // ARRANGE: Mock een mislukte response (bijv. 404) fetch.mockResolvedValueOnce({ ok: false, status: 404, }); // ACT & ASSERT: Gebruik `rejects.toThrow` om de fout te testen // Let op: je moet de assertion returnen of awaiten await expect(fetchData('https://api.example.com/invalid')) .rejects .toThrow('HTTP error! status: 404'); }); }); ``` #### Testen met **Promises** (minder gebruikelijk, maar goed om te weten): ```javascript it('should return data using promises', () => { const mockData = { title: 'Test Post' }; fetch.mockResolvedValueOnce({ ok: true, json: async () => mockData, }); // Belangrijk: return de promise naar de testrunner return fetchData('https://api.example.com/data').then(result => { expect(result).toEqual(mockData); }); }); ``` #### Testen met **Callbacks** (voor legacy-code): ```javascript it('should work with callbacks', (done) => { // `done` callback als parameter someOldCallbackFunction((err, data) => { // Als je done niet aanroept, faalt de test na een timeout try { expect(err).toBeNull(); expect(data).toBe('expected value'); done(); // Succes: roep done zonder argument aan } catch (error) { done(error); // Faal de test: geef de error mee aan done } }); }); ``` --- ### 3. Essentiële Tools & Technieken #### 1. Mocking (Het allerbelangrijkste!) Asynchrone code heeft vaak externe afhankelijkheden (netwerk, database, filesystem). **Je moet deze altijd mocken** voor unit tests. * **Jest/Vitest:** Hebben uitstekende, ingebouwde mocking capabilities. * `jest.fn()`, `jest.spyOn()`, `jest.mock()` * `.mockResolvedValue(value)` voor async functions (verkort voor `.mockImplementation(() => Promise.resolve(value))`). * `.mockRejectedValue(error)` om een fout te simuleren. * **Dedicated Mocking Libraries:** * `sinon.js`: Zeer krachtig voor het maken van spies, stubs en mocks. * `testdouble.js`: Een alternatief met een andere filosofie. #### 2. Test Runners & Frameworks * **Jest:** De *de facto* standaard. Heeft alles ingebouwd: testrunner, assertions, mocking, code coverage. * **Vitest:** De moderne, snelle opvolger, zeer compatibel met Jest API en perfect voor Vite projects. * **Mocha + Chai + Sinon:** Een klassieke, modulaire combinatie. Je kiest zelf je assertion- en mocking-library. --- ### 4. Best Practices om Flakiness (Toevallige Falen) te Voorkomen 1. **Mock Alles Wat Extern Is:** * **Netwerk calls:** Mock `fetch`, `axios`, of andere HTTP-clients. Gebruik nooit een echte API in unit tests. * **Databases:** Mock je database-laag (bijv. met een in-memory implementatie of een mock object). * **Timers (`setTimeout`, `setInterval`):** Gebruik **"Fake Timers"** (Jest: `jest.useFakeTimers()`). Dit versnelt je tests enorm en maakt ze deterministisch. 2. **Wees Specifiek met Timeouts:** * Stel een langere timeout in voor specifieke, langlopende tests met `jest.setTimeout(10000) // 10 seconden`, maar probeer eerst de oorzaak van de traagheid op te lossen (meestal niet-gemockte I/O). 3. **Test Zowel de Success- als de Error-Cases:** * Test niet alleen het "gelukkige pad", maar forceer ook foutcondities in je mocks (`.mockRejectedValue(...)`) om te zien of je code de errors correct afhandelt. 4. **Gebruik `async`/`await` Consistently:** * Het is het duidelijkst en leidt tot de minste fouten. Vermijd het mixen van `.then()` en `async/await` in dezelfde test. 5. **Isolate Your Tests:** * Zorg ervoor dat elke test onafhankelijk is. Reset je mocks (`mockClear()`, `mockReset()`) in `beforeEach` of `afterEach` hooks. Dit voorkomt dat de status van de ene test de resultaten van een andere test beïnvloedt. 6. **Controleer of Functies Zijn Aangeroepen (Interaction Testing):** * Naast het testen van return values, is het vaak cruciaal om te verifiëren *of* en *hoe* een gemockte functie is aangeroepen (`expect(fetch).toHaveBeenCalledWith(expectedUrl)`). ### Samenvattend Stappenplan voor jouw Project: 1. **Kies een Testrunner:** Jest of Vitest zijn uitstekende keuzes. 2. **Identificeer Afhankelijkheden:** Welke externe services gebruikt je asynchrone code? (API's, DB's, etc.) 3. **Mock deze Afhankelijkheden:** Gebruik `jest.fn()` en `.mockResolvedValue()` om hun gedrag te controleren. 4. **Schrijf Tests met `async/await`:** Gebruik `await` om het resultaat van je functie op te halen en `expect` om het te controleren. 5. **Test Expliciet op Fouten:** Gebruik `await expect(...).rejects.toThrow()`. 6. **Gebruik Hooks voor Setup/Schoonmaak:** `beforeEach` om mocks te resetten en een schone staat te garanderen. 7. **Vervang Echte Timers door Fake Timers** voor tests die met wachttijden te maken hebben. Door deze strategie te volgen, krijg je een suite van snelle, betrouwbare en onderhoudbare tests voor je asynchrone code.