Mocking API Endpoints with Jest
This challenge focuses on creating a robust way to mock API endpoints within your Jest test suite. Effectively stubbing external services allows you to isolate your code, ensure test predictability, and speed up your test execution by avoiding actual network requests. You will implement a system to define and manage mock responses for HTTP requests.
Problem Description
Your task is to create a "stub server" using Jest to mock HTTP requests. This stub server should intercept outgoing HTTP requests made by your application during testing and return predefined responses. This allows you to test components that rely on external APIs without actually making network calls.
Key Requirements:
- Intercept Requests: The stub server must be able to intercept HTTP requests (e.g., GET, POST, PUT, DELETE) made by your application code.
- Define Mock Responses: You should be able to define specific mock responses for different URL paths and HTTP methods. This includes setting the status code, response body (JSON or plain text), and custom headers.
- Handle Different Methods: The stub server should support common HTTP methods.
- Reset Mocks: Provide a mechanism to easily clear and reset all defined mocks between test runs or test cases.
- Integration with Jest: The stubbing mechanism should integrate seamlessly with Jest, ideally using its mocking capabilities.
Expected Behavior:
When your application code makes an HTTP request that matches a configured mock:
- The request should be intercepted.
- The predefined status code, response body, and headers should be returned.
- The actual network request should not be made.
If a request does not match any configured mock, it should either be allowed to proceed (in a development or integration testing environment) or potentially fail (in a unit testing scenario, depending on the setup). For this challenge, assume unmatched requests should result in an error or be explicitly ignored.
Edge Cases:
- Requests to different HTTP methods on the same URL path.
- Requests with different query parameters or request bodies (though for this challenge, matching on URL path and method is sufficient).
- Handling of different content types in responses.
Examples
Example 1: Mocking a GET Request
Let's say your application makes a GET request to /api/users/123.
- Input (during test execution): An HTTP
GETrequest tohttp://localhost:3000/api/users/123. - Mock Configuration:
stubServer.mockGet('/api/users/123', { statusCode: 200, body: { id: 123, name: 'Alice' }, headers: { 'Content-Type': 'application/json' } }); - Output (returned to application code):
- Status Code:
200 - Response Body:
{ id: 123, name: 'Alice' } - Headers:
{ 'Content-Type': 'application/json' }
- Status Code:
- Explanation: The stub server intercepts the
GETrequest to the specified URL and returns the configured JSON object as the response body with a 200 status code.
Example 2: Mocking a POST Request with Different Response
Imagine a POST request to /api/products.
- Input (during test execution): An HTTP
POSTrequest tohttp://localhost:3000/api/productswith a request body like{ name: 'New Gadget', price: 99.99 }. - Mock Configuration:
stubServer.mockPost('/api/products', { statusCode: 201, body: { id: 'abc-456', name: 'New Gadget', price: 99.99 }, headers: { 'Content-Type': 'application/json' } }); - Output (returned to application code):
- Status Code:
201 - Response Body:
{ id: 'abc-456', name: 'New Gadget', price: 99.99 } - Headers:
{ 'Content-Type': 'application/json' }
- Status Code:
- Explanation: The stub server intercepts the
POSTrequest and returns a201 Createdstatus with the new product's details.
Example 3: Handling Unmatched Requests
If a GET request is made to /api/settings but no mock is defined for it.
- Input (during test execution): An HTTP
GETrequest tohttp://localhost:3000/api/settings. - Mock Configuration: (No configuration for
/api/settings) - Output (returned to application code): An error is thrown, indicating an unexpected request.
- Explanation: Since no mock was defined for this specific request, the stub server flags it as an unhandled request, which is crucial for ensuring tests only interact with intended mocked endpoints.
Constraints
- Your solution should primarily leverage Jest's mocking capabilities (e.g.,
jest.fn(),jest.spyOn(), or potentially libraries that integrate with Jest likejest-fetch-mockormswif you choose to explore them). - The stub server should be designed to be easily set up and torn down within Jest's
beforeEachandafterEachhooks. - The mock definition structure should be clear and concise.
- Performance is important; the stubbing mechanism should not significantly slow down your test suite.
Notes
Consider how you will manage the state of your stub server. It should be cleared before each test to prevent mocks from one test affecting another. Think about the underlying HTTP client your application uses (e.g., fetch, axios) and how you can intercept calls made through it. You might need to write a small wrapper or use a library designed for this purpose. The goal is to create a reusable pattern for mocking.