UNPKG

undici

Version:

An HTTP/1.1 client, written from scratch for Node.js

428 lines (301 loc) 11.8 kB
# Class: MockPool Extends: `undici.Pool` A mock Pool class that implements the Pool API and is used by MockAgent to intercept real requests and return mocked responses. ## `new MockPool(origin, [options])` Arguments: * **origin** `string` - It should only include the **protocol, hostname, and port**. * **options** `MockPoolOptions` - It extends the `Pool` options. Returns: `MockPool` ### Parameter: `MockPoolOptions` Extends: `PoolOptions` * **agent** `Agent` - the agent to associate this MockPool with. ### Example - Basic MockPool instantiation We can use MockAgent to instantiate a MockPool ready to be used to intercept specified requests. It will not do anything until registered as the agent to use and any mock request are registered. ```js 'use strict' const { MockAgent } = require('undici') const mockAgent = new MockAgent() const mockPool = mockAgent.get('http://localhost:3000') ``` ## Instance Methods ### `MockPool.intercept(options)` This method defines the interception rules for matching against requests for a MockPool or MockPool. We can intercept multiple times on a single instance. When defining interception rules, all the rules must pass for a request to be intercepted. If a request is not intercepted, a real request will be attempted. | Matcher type | Condition to pass | |:------------:| -------------------------- | | `string` | Exact match against string | | `RegExp` | Regex must pass | | `Function` | Function must return true | Arguments: * **options** `MockPoolInterceptOptions` - Interception options. Returns: `MockInterceptor` corresponding to the input options. ### Parameter: `MockPoolInterceptOptions` * **path** `string | RegExp | (path: string) => boolean` - a matcher for the HTTP request path. * **method** `string | RegExp | (method: string) => boolean` - a matcher for the HTTP request method. * **body** `string | RegExp | (body: string) => boolean` - (optional) - a matcher for the HTTP request body. * **headers** `Record<string, string | RegExp | (body: string) => boolean`> - (optional) - a matcher for the HTTP request headers. To be intercepted, a request must match all defined headers. Extra headers not defined here may (or may not) be included in the request and do not affect the interception in any way. ### Return: `MockInterceptor` We can define the behaviour of an intercepted request with the following options. * **reply** `(statusCode: number, replyData: string | object, responseOptions?: MockResponseOptions) => MockScope` - define a reply for a matching request. Default for `responseOptions` is `{}`. * **replyWithError** `(error: Error) => MockScope` - define an error for a matching request to throw. * **defaultReplyHeaders** `(headers: Record<string, string>) => MockInterceptor` - define default headers to be included in subsequent replies. These are in addition to headers on a specific reply. * **defaultReplyTrailers** `(trailers: Record<string, string>) => MockInterceptor` - define default trailers to be included in subsequent replies. These are in addition to trailers on a specific reply. * **replyContentLength** `() => MockInterceptor` - define automatically calculated `content-length` headers to be included in subsequent replies. By default, `reply` and `replyWithError` define the behaviour for the first matching request only. Subsequent requests will not be affected (this can be changed using the returned `MockScope`). ### Parameter: `MockResponseOptions` * **headers** `Record<string, string>` - headers to be included on the mocked reply. * **trailers** `Record<string, string>` - trailers to be included on the mocked reply. ### Return: `MockScope` A `MockScope` is associated with a single `MockInterceptor`. With this, we can configure the default behaviour of a intercepted reply. * **delay** `(waitInMs: number) => MockScope` - delay the associated reply by a set amount in ms. * **persist** `() => MockScope` - any matching request will always reply with the defined response indefinitely. * **times** `(repeatTimes: number) => MockScope` - any matching request will reply with the defined response a fixed amount of times. This is overridden by **persist**. #### Example - Basic Mocked Request ```js 'use strict' const { MockAgent } = require('undici') const mockAgent = new MockAgent({ connections: 1 }) setGlobalDispatcher(mockAgent) // MockPool const mockPool = mockAgent.get('http://localhost:3000') mockPool.intercept({ path: '/foo', method: 'GET', }).reply(200, 'foo') const { statusCode, body } = await request('http://localhost:3000/foo') console.log('response received', statusCode) // 200 for await (const data of body) { console.log('data', data) // 'foo' } ``` #### Example - Basic Mocked requests with multiple intercepts ```js 'use strict' const { MockAgent } = require('undici') const mockAgent = new MockAgent({ connections: 1 }) setGlobalDispatcher(mockAgent) const mockPool = mockAgent.get('http://localhost:3000') mockPool.intercept({ path: '/foo', method: 'GET', }).reply(200, 'foo') mockPool.intercept({ path: '/hello', method: 'GET', }).reply(200, 'hello') const result1 = await request('http://localhost:3000/foo') const result2 = await request('http://localhost:3000/hello') ``` #### Example - Mocked request with query body, request headers and response headers and trailers ```js 'use strict' const { MockAgent } = require('undici') const mockAgent = new MockAgent({ connections: 1 }) setGlobalDispatcher(mockAgent) const mockPool = mockAgent.get('http://localhost:3000') mockPool.intercept({ path: '/foo?hello=there&see=ya', method: 'POST', body: 'form1=data1&form2=data2', headers: { 'User-Agent': 'undici', Host: 'example.com' } }).reply(200, { foo: 'bar' }, { headers: { 'content-type': 'application/json' }, trailers: { 'Content-MD5': 'test' } }) const { statusCode, headers, tailers, body } = await request('http://localhost:3000/foo?hello=there&see=ya', { method: 'POST', body: 'form1=data1&form2=data2', headers: { foo: 'bar', 'User-Agent': 'undici', Host: 'example.com' } }) console.log('response received', statusCode) // 200 console.log('headers', headers) // {"content-type":"application/json"} for await (const data of body) { console.log('data', data) // '{"foo":"bar"}' } console.log('trailers', trailers) // {"Content-MD5":"test"} ``` #### Example - Mocked request using different matchers ```js 'use strict' const { MockAgent } = require('undici') const mockAgent = new MockAgent({ connections: 1 }) setGlobalDispatcher(mockAgent) const mockPool = mockAgent.get('http://localhost:3000') mockPool.intercept({ path: '/foo', method: /^GET$/, body: (value) => value === 'form=data', headers: { 'User-Agent': 'undici', Host: /^example.com$/ } }).reply(200, 'foo') const result = await request('http://localhost:3000/foo', { method: 'GET', body: 'form=data', headers: { foo: 'bar', 'User-Agent': 'undici', Host: 'example.com' } }) // Will match and return mocked data ``` #### Example - Mocked request with reply with a defined error ```js 'use strict' const { MockAgent } = require('undici') const mockAgent = new MockAgent({ connections: 1 }) setGlobalDispatcher(mockAgent) const mockPool = mockAgent.get('http://localhost:3000') mockPool.intercept({ path: '/foo', method: 'GET' }).replyWithError(new Error('kaboom')) await request('http://localhost:3000/foo', { method: 'GET', }) // Will throw new Error('kaboom') ``` #### Example - Mocked request with defaultReplyHeaders ```js 'use strict' const { MockAgent } = require('undici') const mockAgent = new MockAgent({ connections: 1 }) setGlobalDispatcher(mockAgent) const mockPool = mockAgent.get('http://localhost:3000') mockPool.intercept({ path: '/foo', method: 'GET', }).defaultReplyHeaders({ foo: 'bar' }) .reply(200, 'foo') const { headers } = await request('http://localhost:3000/foo') // headers: {"foo":"bar"} ``` #### Example - Mocked request with defaultReplyTrailers ```js 'use strict' const { MockAgent } = require('undici') const mockAgent = new MockAgent({ connections: 1 }) setGlobalDispatcher(mockAgent) const mockPool = mockAgent.get('http://localhost:3000') mockPool.intercept({ path: '/foo', method: 'GET', }).defaultReplyTrailers({ foo: 'bar' }) .reply(200, 'foo') const { trailers } = await request('http://localhost:3000/foo') // trailers: {"foo":"bar"} ``` #### Example - Mocked request with automatic content-length calculation ```js 'use strict' const { MockAgent } = require('undici') const mockAgent = new MockAgent({ connections: 1 }) setGlobalDispatcher(mockAgent) const mockPool = mockAgent.get('http://localhost:3000') mockPool.intercept({ path: '/foo', method: 'GET' }).replyContentLength().reply(200, 'foo') const { headers } = await request('http://localhost:3000/foo') // headers: {"content-length":"3"} ``` #### Example - Mocked request with automatic content-length calculation on an object ```js 'use strict' const { MockAgent } = require('undici') const mockAgent = new MockAgent({ connections: 1 }) setGlobalDispatcher(mockAgent) const mockPool = mockAgent.get('http://localhost:3000') mockPool.intercept({ path: '/foo', method: 'GET' }).replyContentLength().reply(200, 'foo') const { headers } = await request('http://localhost:3000/foo') // headers: {"content-length":"3"} ``` #### Example - Mocked request with persist enabled ```js 'use strict' const { MockAgent } = require('undici') const mockAgent = new MockAgent({ connections: 1 }) setGlobalDispatcher(mockAgent) const mockPool = mockAgent.get('http://localhost:3000') mockPool.intercept({ path: '/foo', method: 'GET' }).reply(200, 'foo').persist() const result1 = await request('http://localhost:3000/foo') // Will match and return mocked data const result2 = await request('http://localhost:3000/foo') // Will match and return mocked data // Etc ``` #### Example - Mocked request with times enabled ```js 'use strict' const { MockAgent } = require('undici') const mockAgent = new MockAgent({ connections: 1 }) setGlobalDispatcher(mockAgent) const mockPool = mockAgent.get('http://localhost:3000') mockPool.intercept({ path: '/foo', method: 'GET' }).reply(200, 'foo').times(2) const result1 = await request('http://localhost:3000/foo') // Will match and return mocked data const result2 = await request('http://localhost:3000/foo') // Will match and return mocked data const result3 = await request('http://localhost:3000/foo') // Will not match and make attempt a real request ``` ### `MockPool.close()` Closes the mock pool and de-registers from associated MockAgent. Returns: `Promise<void>` #### Example - clean up after tests are complete ```js 'use strict' const { MockAgent } = require('undici') const mockAgent = new MockAgent({ connections: 1 }) const mockPool = mockAgent.get('http://localhost:3000') await mockPool.close() ``` ### `MockPool.dispatch(options, handlers)` Implements [`Dispatcher.dispatch(options, handlers)`](docs/api/Dispatcher.md#clientdispatchoptions-handlers). ### `MockPool.request(options[, callback])` See [`Dispatcher.request(options [, callback])`](docs/api/Dispatcher.md#clientrequestoptions--callback). #### Example - MockPool request ```js 'use strict' const { MockAgent } = require('undici') const mockAgent = new MockAgent() const mockClient = mockAgent.get('http://localhost:3000') mockClient.intercept({ path: '/foo', method: 'GET', }).reply(200, 'foo') const { statusCode, body } = await mockClient.request({ origin: 'http://localhost:3000', path: '/foo', method: 'GET' }) ```