request-mocking-protocol v0.1.1
Request Mocking Protocol (RMP)
A protocol for declarative mocking of HTTP requests. Useful in E2E testing when you need to mock API calls made on the server (e.g., in React Server Components).
Index
How it works
flowchart LR;
A(Test Runner) -- "x-mock-request:<br><code style='font-size: 0.8em;padding: 0 10px'>{url:#quot;http:\/\/external/api#quot;,body:#quot;Hello#quot;}</code></span>" --> B(App Server);
B -- <h1>Hello</h1> --> A;
B -. Mocked! .-> C(External API);
C -.-> B;
- The mock defines a request and response in a serializable JSON format.
- When opening a webpage, the mock is attached to the navigation request as a custom HTTP header.
- On the server, an interceptor reads the mock header and applies mock to the outbound API requests.
- The application server renders the page using data from the mocked response.
Concepts
Request Schema
The request schema is a serializable object that defines parameters for matching a request.
Example:
{
method: 'GET',
url: 'https://jsonplaceholder.typicode.com/users',
query: {
foo: 'bar'
}
}
This schema will match the request:
GET https://jsonplaceholder.typicode.com/users?foo=bar
Response Schema
The response schema is a serializable object that defines how to build the mocked response.
Example:
{
status: 200,
body: 'Hello world'
}
Transport
Transport allows passing mock schemas from the test runner to the application server.
RMP uses a custom HTTP header x-mock-request
for transferring a JSON-stringified schemas list.
Example:
x-mock-request: [{"reqSchema":{"method":"GET","patternType":"urlpattern","url":"https://example.com"},"resSchema":{"body":"hello","status":200}}]
On the server side, the interceptor will read inbound headers and apply mocks.
Installation
npm i -D request-mocking-protocol
Usage
To mock requests with RMP, you need to perform two actions:
- Setup a request interceptor in the application.
- Define mocks in your test using the
MockClient
class.
Check out the sections below for integration with varios frameworks and test-runners.
App Integration
Next.js
Add the following code to the instrumentation.ts file:
import { headers } from 'next/headers';
export async function register() {
if (process.env.NEXT_RUNTIME === 'nodejs' && process.env.NODE_ENV !== 'production') {
const { setupFetchInterceptor } = await import('request-mocking-protocol/fetch');
setupFetchInterceptor(() => headers());
}
}
Astro
See astro.config.ts in the astro-cypress example.
Test-runners Integration
On the test runner side, you can define request mocks via the MockClient
class.
Playwright
Set up the mockServerRequest
fixture:
import { test as base } from '@playwright/test';
import { MockClient } from 'request-mocking-protocol';
export const test = base.extend<{ mockServerRequest: MockClient }>({
mockServerRequest: async ({ context }, use) => {
const mockClient = new MockClient();
mockClient.onChange = async (headers) => context.setExtraHTTPHeaders(headers);
await use(mockClient);
},
});
Use the mockServerRequest
fixture to define server-side request mocks:
test('my test', async ({ page, mockServerRequest }) => {
await mockServerRequest.GET('https://jsonplaceholder.typicode.com/users', {
body: [{ id: 1, name: 'John Smith' }],
});
// ...
});
Cypress
Add a custom command
mockServerRequest
in support files, see example mock-server-request.js.Use the custom command to define mocks:
it('shows list of users', () => { cy.mockServerRequest('https://jsonplaceholder.typicode.com/users', { body: [{ id: 1, name: 'John Smith' }], }); // ... });
API
Interceptors
Interceptors are used on the server side to capture HTTP requests and apply mocks. Currently, there are two interceptors available.
Fetch
This interceptor hooks into globalThis.fetch
.
Basic usage:
const { setupFetchInterceptor } = await import('request-mocking-protocol/fetch');
setupFetchInterceptor(() => { /* retrieve headers of the inbound HTTP request */ });
The actual function for retrieving inbound headers depends on the application framework.
MSW
You can use MSW to intercept server-side requests:
import { setupServer } from 'msw/node';
import { createHandler } from 'request-mocking-protocol/msw';
const mockHandler = createHandler(() => { /* retrieve headers of the inbound HTTP request */ });
const mswServer = setupServer(mockHandler);
mswServer.listen();
The actual function for retrieving inbound headers depends on the application framework.
MockClient
The MockClient
class is used on the test-runner side to define HTTP request mocks.
Constructor
constructor(options?: MockClientOptions)
Creates a new instance of MockClient
.
options
(optional): An object containing configuration options.debug
(optional): A boolean indicating whether to enable debug mode.defaultMethod
(optional): The default HTTP method to use for requests.
Properties
headers: Record<string, string>
Returns HTTP headers that are built from the mock schemas. Can be sent to the server for mocking server-side requests.
onChange?: (headers: Record<string, string>) => unknown
A callback function that is called whenever the mocks are changed.
Methods
async addMock(reqSchema, resSchema): Promise<void>
async GET(reqSchema, resSchema): Promise<void>
async POST(reqSchema, resSchema): Promise<void>
async PUT(reqSchema, resSchema): Promise<void>
async DELETE(reqSchema, resSchema): Promise<void>
async HEAD(reqSchema, resSchema): Promise<void>
async ALL(reqSchema, resSchema): Promise<void>
Adds a new mock for the corresponding HTTP method.
reqSchema
: The request schema to add.resSchema
: The response schema to add.
async reset(): Promise<void>
Clears all mock schemas and rebuilds the headers.