Life-cycle events
The life-cycle events API allows you to attach listeners to the internal library events, such as when a new request is made or a mocked response is sent. This API is primarily designed for third-party extensions or for debugging request handlers.
Life-cycle events happen post-factum and cannot affect related requests/responses.
setupWorker
/setupServer
instead.Usage
The life-cycle events API is exposed under the .events
property of the worker
/server
instance:
1const worker = setupWorker(...handlers)2worker.events34// or56const server = setupServer(...handlers)7server.events
The API itself resembles the EventEmitter but is stripped down to the following methods:
on
, to react to a given event;removeListener
, to remove a listener for a given event;removeAllListeners
, to remove all listeners.
Such limitation is intended, as you mustn't emit internal events—that is done for you by the library.
Methods
on
Use the .on
method to add an event listener to a given life-cycle event.
1worker.events.on('request:start', (req) => {2 console.log(req.method, req.url.href)3})
removeListener
Removes the listener for the given life-cycle event.
Removing listeners is particularly handy to reset the events introspection during tests.
1// Previously attached listener.2const listener = () => console.log('new request')3worker.events.on('request:start', listener)45worker.events.removeListener('request:start', listener)
removeAllListeners
Removes all life-cycle events listeners.
1worker.events.removeAllListeners()
Optionally, accepts a life-cycle event name to specify which group of listeners to remove, in case there are multiple.
1// Removes all listeners for the "request:start" event,2// but keeps all the other listeners.3worker.events.removeAllListeners('request:start')
Events
request:start
A new request has been dispatched.
Parameter name | Type | Description |
---|---|---|
req | MockedRequest | A request representation. |
1worker.events.on('request:start', (req) => {2 console.log('new request:', req.method, req.url.href)3})
request:match
A dispatched request has a corresponding request handler.
Parameter name | Type | Description |
---|---|---|
req | MockedRequest | A request representation. |
1worker.events.on('request:match', (req) => {2 console.log('%s %s has a handler!', req.method, req.url.href)3})
request:unhandled
A dispatched request doesn't have any corresponding request handler.
Parameter name | Type | Description |
---|---|---|
req | MockedRequest | A request representation. |
1worker.events.on('request:unhandled', (req) => {2 console.log('%s %s has no handler', req.method, req.url.href)3})
request:end
A request has ended. Emits for all requests regardless if their responses were mocked or bypassed.
Parameter name | Type | Description |
---|---|---|
req | MockedRequest | A request representation. |
1worker.events.on('request:end', (req) => {2 console.log('%s %s ended', req.method, req.url.href)3})
response:mocked
A mocked response has been sent.
Parameter name | Type | Description |
---|---|---|
res | Response (worker), IsomorphicResponse (server) | The response that's been sent. |
reqId | string | UUID of the associated request. |
1worker.events.on('response:mocked', async (res, reqId) => {2 const responseText = await res.text()3 console.log('sent a mocked response', reqId, responseText)4})
res
instance differs between the browser and Node.js. Take this difference into account when operating with it.1server.events.on('response:mocked', (res, reqId) => {2 const responseText = res.body3 console.log('sent a mocked response', reqId, responseText)4})
response:bypass
An original response has been sent.
Parameter name | Type | Description |
---|---|---|
res | Response (worker), IsomorphicResponse (server) | The response that's been sent. |
reqId | string | UUID of the associated request. |
1worker.events.on('response:bypass', async (res, reqId) => {2 const responseText = await res.text()3 console.log('sent an original response', reqId, responseText)4})
Just as with the response:mocked
event, the res
instance is different between the browser and Node.js.
1server.events.on('response:bypass', (res, reqId) => {2 const responseText = res.body3 console.log('sent an original response', reqId, responseText)4})
Examples
Tracking a request
Although each life-cycle events emits in isolation, it includes a request ID (reqId
) parameter to connect related requests and responses.
1const allRequests = new Map()23worker.events.on('request:start', (req) => {4 const { id } = req5 // Store this request by id in an internal Map.6 allRequests.set(id, req)7})89worker.events.on('response:mocked', (res, reqId) => {10 // Get a request associated with this response.11 const req = allRequests.get(reqId)12})
Asserting request payload
The life-cycle events API can be used for retrieving a request reference and writing request assertions (i.e. whether the correct request payload was sent).
1import { matchRequestUrl, MockedRequest } from 'msw'2import { setupServer } from 'msw/node'3import { handlers } from './handlers'45const server = setupServer(...handlers)67function waitForRequest(method: string, url: string) {8 let requestId = ''910 return new Promise<MockedRequest>((resolve, reject) => {11 server.events.on('request:start', (req) => {12 const matchesMethod = req.method.toLowerCase() === method.toLowerCase()13 const matchesUrl = matchRequestUrl(req.url, url).matches1415 if (matchesMethod && matchesUrl) {16 requestId = req.id17 }18 })1920 server.events.on('request:match', (req) => {21 if (req.id === requestId) {22 resolve(req)23 }24 })2526 server.events.on('request:unhandled', (req) => {27 if (req.id === requestId) {28 reject(29 new Error(`The ${req.method} ${req.url.href} request was unhandled.`),30 )31 }32 })33 })34}3536beforeAll(() => server.listen())37afterAll(() => server.close())3839it('sends the user credentials to the backend', async () => {40 // Establish a request listener but don't resolve it yet.41 const pendingRequest = waitForRequest('POST', '/login')4243 // Perform the request itself.44 // Here you would use any tested code that makes a request.45 await fetch('/login', {46 method: 'POST',47 headers: {48 'Content-Type': 'application/json',49 },50 body: JSON.stringify({51 email: 'user@example.com',52 password: 'super+secret123',53 }),54 })5556 // Await the request and get its reference.57 const request = await pendingRequest5859 expect(request.body).toEqual({60 email: 'user@example.com',61 password: 'super+secret123',62 })63})