
In modern web applications, testing is crucial for maintaining quality and ensuring the user experience remainssmooth and reliable. One of the challenges developers often face when writing automated tests with tools like Cypress is handling API responses that may change dynamically. In this blog post, we’ll dive into creating a reusable Cypress command that intercepts API calls, modifies their responses dynamically, and allows for flexible adjustments based on specific test scenarios.
Why Modify API Responses in Tests?
There are several reasons why you might want to intercept and modify API responses in your Cypress tests:
- Simulating Different Server States: You may need to simulate various server responses to test how your application handles different conditions (e.g., successful data fetch, data not found, or server errors).
- Isolating Tests from Backend Changes: When running tests in an environment where backend data frequently changes, modifying responses ensures tests remain consistent.
- Testing Edge Cases: Sometimes, testing specific scenarios (like expired sessions or unique data conditions) may not be straightforward. Modifying API responses on the fly can help simulate these situations effectively.
The Problem
Suppose you have an API endpoint that retrieves customer order data, and the API response includes a scheduleDate field. Your application behaviour depends on whether the scheduleDate is today or a future date. During testing, you may want to modify this date to check how your application handles different scheduling scenarios.
To achieve this, we’ll build a Cypress command that:
- Intercepts requests based on a dynamic HTTP method and URL pattern.
- Modifies specific fields in the API response based on provided functions.
- Allows easy reuse across different tests.
Introducing the Solution
The solution involves creating a custom Cypress command called interceptAndModifyResponse. This command will allow you to specify:
- The HTTP method (e.g., GET, POST, PUT, etc.).
- A URL pattern to match the API endpoint.
- An alias for easy reference in tests.
- A set of modifications to apply to the response body. Let's break down the implementation step by step.
Step 1: Creating the Cypress Command
Here’s the code for our reusable Cypress command:
Typescriptimport { Method } from 'cypress'; Cypress.Commands.add( 'interceptAndModifyResponse', ( method: Method, urlPattern: string | RegExp, alias: string, modifications: Record<string, (value: any)=> any> ) => { // Intercept the request using the specified method and URL pattern cy.intercept({ method, url: urlPattern }, (req) => { req.reply((res) => { // Ensure response body exists and is an object if (!res.body || typeof res.body !== 'object') { console.warn(`Response body is not an object for alias: ${alias}`); return res; } // Apply modifications to the response body fields Object.entries(modifications).forEach(([key, modifyFn]) => { if (res.body[key] !== undefined) { try { res.body[key] = modifyFn(res.body[key]); } catch (error) { console.error(`Error modifying key \"${key}\":`, error); } } }); return res; }); }).as(alias); } );
Great job! Now let's reinforce your learning with some flashcards for key React concepts:
Step 2: TypeScript Declaration (Optional)
If you’re using TypeScript, adding a type declaration for this custom command can improve type safety:
Typescriptdeclare namespace Cypress { interface Chainable { interceptAndModifyResponse( method: Method, urlPattern: string | RegExp, alias: string, modifications: Record<string, (value: any)=> any> ): Chainable<void>; } }
Step 3: How the Command Works
This custom command works as follows:
- Intercept: It intercepts HTTP requests that match the provided method and URL pattern.
- Modify: When a response is received, it modifies specified fields using the provided transformation functions.
- Alias: Assigns an alias to the intercepted request, allowing you to wait for or assert against it in your tests.
Step 4: Using the Custom Command in Tests
Here are a few examples of how you can use the new command in your Cypress tests:
Example 1: Intercepting and Modifying a GET Request
Imagine you have an endpoint that returns order details, and you want to modify the scheduleDate if it matches today’s date.
Typescriptbefore(() => { cy.interceptAndModifyResponse('GET', '/ops/customer/orders/[0-9]+', 'orderData', { scheduleDate: (currentDate) => { const today = new Date(); const scheduleDate = new Date(currentDate); return scheduleDate.toDateString() === today.toDateString() ? new Date().toISOString() : currentDate; }, : currentStatus), }); status: (currentStatus) => (currentStatus === 'pending' ? 'confirmed' }); it('should handle orders with updated schedule dates correctly', () => { cy.visit('/orders'); cy.wait('@orderData'); cy.get('.order-status').should('contain', 'confirmed'); });
Example 2: Intercepting a POST Request
Let’s say you want to modify the response of a POST request that creates a new order.
Typescriptbefore(() => { cy.interceptAndModifyResponse('POST', /\/ops\/customer\/orders/, 'createOrder', { orderNumber: (num) => `ORD-${num}` , createdAt: () => new Date().toISOString(), }); }); it('should create an order with the modified response', () => { cy.visit('/new-order'); cy.get('#create-order-button').click(); cy.wait('@createOrder').its('response.body.orderNumber').should('contain', 'ORD-'); });
Benefits of This Approach
- Reusable: The custom command is flexible and reusable, making it easy to modify different endpoints across multiple tests.
- Scalable: Supports different HTTP methods (GET, POST, etc.) and handles both string and regex URL patterns.
- Maintainable: Keeps your tests clean and reduces redundancy by centralising response modifications.
Conclusion
Intercepting and modifying API responses can be incredibly useful in your Cypress tests, allowing you to simulate various scenarios and isolate your tests from backend data changes.
TheinterceptAndModifyResponse command we created in this blog provides a powerful and reusable way to handle dynamic API responses efficiently.
By leveraging Cypress’s built-in capabilities along with custom commands, you can enhance the robustness of your test suite and reduce flakiness, ensuring a more reliable CI/CD pipeline.
I hope you found this blog helpful! Try out this approach in your projects, and feel free to tweak it to fit your testing needs. Happy testing!