source: node_modules/undici/docs/api/MockPool.md

main
Last change on this file was d24f17c, checked in by Aleksandar Panovski <apano77@…>, 15 months ago

Initial commit

  • Property mode set to 100644
File size: 15.8 KB
RevLine 
[d24f17c]1# Class: MockPool
2
3Extends: `undici.Pool`
4
5A mock Pool class that implements the Pool API and is used by MockAgent to intercept real requests and return mocked responses.
6
7## `new MockPool(origin, [options])`
8
9Arguments:
10
11* **origin** `string` - It should only include the **protocol, hostname, and port**.
12* **options** `MockPoolOptions` - It extends the `Pool` options.
13
14Returns: `MockPool`
15
16### Parameter: `MockPoolOptions`
17
18Extends: `PoolOptions`
19
20* **agent** `Agent` - the agent to associate this MockPool with.
21
22### Example - Basic MockPool instantiation
23
24We 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.
25
26```js
27import { MockAgent } from 'undici'
28
29const mockAgent = new MockAgent()
30
31const mockPool = mockAgent.get('http://localhost:3000')
32```
33
34## Instance Methods
35
36### `MockPool.intercept(options)`
37
38This method defines the interception rules for matching against requests for a MockPool or MockPool. We can intercept multiple times on a single instance, but each intercept is only used once. For example if you expect to make 2 requests inside a test, you need to call `intercept()` twice. Assuming you use `disableNetConnect()` you will get `MockNotMatchedError` on the second request when you only call `intercept()` once.
39
40When 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.
41
42| Matcher type | Condition to pass |
43|:------------:| -------------------------- |
44| `string` | Exact match against string |
45| `RegExp` | Regex must pass |
46| `Function` | Function must return true |
47
48Arguments:
49
50* **options** `MockPoolInterceptOptions` - Interception options.
51
52Returns: `MockInterceptor` corresponding to the input options.
53
54### Parameter: `MockPoolInterceptOptions`
55
56* **path** `string | RegExp | (path: string) => boolean` - a matcher for the HTTP request path. When a `RegExp` or callback is used, it will match against the request path including all query parameters in alphabetical order. When a `string` is provided, the query parameters can be conveniently specified through the `MockPoolInterceptOptions.query` setting.
57* **method** `string | RegExp | (method: string) => boolean` - (optional) - a matcher for the HTTP request method. Defaults to `GET`.
58* **body** `string | RegExp | (body: string) => boolean` - (optional) - a matcher for the HTTP request body.
59* **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.
60* **query** `Record<string, any> | null` - (optional) - a matcher for the HTTP request query string params. Only applies when a `string` was provided for `MockPoolInterceptOptions.path`.
61
62### Return: `MockInterceptor`
63
64We can define the behaviour of an intercepted request with the following options.
65
66* **reply** `(statusCode: number, replyData: string | Buffer | object | MockInterceptor.MockResponseDataHandler, responseOptions?: MockResponseOptions) => MockScope` - define a reply for a matching request. You can define the replyData as a callback to read incoming request data. Default for `responseOptions` is `{}`.
67* **reply** `(callback: MockInterceptor.MockReplyOptionsCallback) => MockScope` - define a reply for a matching request, allowing dynamic mocking of all reply options rather than just the data.
68* **replyWithError** `(error: Error) => MockScope` - define an error for a matching request to throw.
69* **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.
70* **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.
71* **replyContentLength** `() => MockInterceptor` - define automatically calculated `content-length` headers to be included in subsequent replies.
72
73The reply data of an intercepted request may either be a string, buffer, or JavaScript object. Objects are converted to JSON while strings and buffers are sent as-is.
74
75By 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`).
76
77### Parameter: `MockResponseOptions`
78
79* **headers** `Record<string, string>` - headers to be included on the mocked reply.
80* **trailers** `Record<string, string>` - trailers to be included on the mocked reply.
81
82### Return: `MockScope`
83
84A `MockScope` is associated with a single `MockInterceptor`. With this, we can configure the default behaviour of a intercepted reply.
85
86* **delay** `(waitInMs: number) => MockScope` - delay the associated reply by a set amount in ms.
87* **persist** `() => MockScope` - any matching request will always reply with the defined response indefinitely.
88* **times** `(repeatTimes: number) => MockScope` - any matching request will reply with the defined response a fixed amount of times. This is overridden by **persist**.
89
90#### Example - Basic Mocked Request
91
92```js
93import { MockAgent, setGlobalDispatcher, request } from 'undici'
94
95const mockAgent = new MockAgent()
96setGlobalDispatcher(mockAgent)
97
98// MockPool
99const mockPool = mockAgent.get('http://localhost:3000')
100mockPool.intercept({ path: '/foo' }).reply(200, 'foo')
101
102const {
103 statusCode,
104 body
105} = await request('http://localhost:3000/foo')
106
107console.log('response received', statusCode) // response received 200
108
109for await (const data of body) {
110 console.log('data', data.toString('utf8')) // data foo
111}
112```
113
114#### Example - Mocked request using reply data callbacks
115
116```js
117import { MockAgent, setGlobalDispatcher, request } from 'undici'
118
119const mockAgent = new MockAgent()
120setGlobalDispatcher(mockAgent)
121
122const mockPool = mockAgent.get('http://localhost:3000')
123
124mockPool.intercept({
125 path: '/echo',
126 method: 'GET',
127 headers: {
128 'User-Agent': 'undici',
129 Host: 'example.com'
130 }
131}).reply(200, ({ headers }) => ({ message: headers.get('message') }))
132
133const { statusCode, body, headers } = await request('http://localhost:3000', {
134 headers: {
135 message: 'hello world!'
136 }
137})
138
139console.log('response received', statusCode) // response received 200
140console.log('headers', headers) // { 'content-type': 'application/json' }
141
142for await (const data of body) {
143 console.log('data', data.toString('utf8')) // { "message":"hello world!" }
144}
145```
146
147#### Example - Mocked request using reply options callback
148
149```js
150import { MockAgent, setGlobalDispatcher, request } from 'undici'
151
152const mockAgent = new MockAgent()
153setGlobalDispatcher(mockAgent)
154
155const mockPool = mockAgent.get('http://localhost:3000')
156
157mockPool.intercept({
158 path: '/echo',
159 method: 'GET',
160 headers: {
161 'User-Agent': 'undici',
162 Host: 'example.com'
163 }
164}).reply(({ headers }) => ({ statusCode: 200, data: { message: headers.get('message') }})))
165
166const { statusCode, body, headers } = await request('http://localhost:3000', {
167 headers: {
168 message: 'hello world!'
169 }
170})
171
172console.log('response received', statusCode) // response received 200
173console.log('headers', headers) // { 'content-type': 'application/json' }
174
175for await (const data of body) {
176 console.log('data', data.toString('utf8')) // { "message":"hello world!" }
177}
178```
179
180#### Example - Basic Mocked requests with multiple intercepts
181
182```js
183import { MockAgent, setGlobalDispatcher, request } from 'undici'
184
185const mockAgent = new MockAgent()
186setGlobalDispatcher(mockAgent)
187
188const mockPool = mockAgent.get('http://localhost:3000')
189
190mockPool.intercept({
191 path: '/foo',
192 method: 'GET'
193}).reply(200, 'foo')
194
195mockPool.intercept({
196 path: '/hello',
197 method: 'GET',
198}).reply(200, 'hello')
199
200const result1 = await request('http://localhost:3000/foo')
201
202console.log('response received', result1.statusCode) // response received 200
203
204for await (const data of result1.body) {
205 console.log('data', data.toString('utf8')) // data foo
206}
207
208const result2 = await request('http://localhost:3000/hello')
209
210console.log('response received', result2.statusCode) // response received 200
211
212for await (const data of result2.body) {
213 console.log('data', data.toString('utf8')) // data hello
214}
215```
216
217#### Example - Mocked request with query body, request headers and response headers and trailers
218
219```js
220import { MockAgent, setGlobalDispatcher, request } from 'undici'
221
222const mockAgent = new MockAgent()
223setGlobalDispatcher(mockAgent)
224
225const mockPool = mockAgent.get('http://localhost:3000')
226
227mockPool.intercept({
228 path: '/foo?hello=there&see=ya',
229 method: 'POST',
230 body: 'form1=data1&form2=data2',
231 headers: {
232 'User-Agent': 'undici',
233 Host: 'example.com'
234 }
235}).reply(200, { foo: 'bar' }, {
236 headers: { 'content-type': 'application/json' },
237 trailers: { 'Content-MD5': 'test' }
238})
239
240const {
241 statusCode,
242 headers,
243 trailers,
244 body
245} = await request('http://localhost:3000/foo?hello=there&see=ya', {
246 method: 'POST',
247 body: 'form1=data1&form2=data2',
248 headers: {
249 foo: 'bar',
250 'User-Agent': 'undici',
251 Host: 'example.com'
252 }
253 })
254
255console.log('response received', statusCode) // response received 200
256console.log('headers', headers) // { 'content-type': 'application/json' }
257
258for await (const data of body) {
259 console.log('data', data.toString('utf8')) // '{"foo":"bar"}'
260}
261
262console.log('trailers', trailers) // { 'content-md5': 'test' }
263```
264
265#### Example - Mocked request using different matchers
266
267```js
268import { MockAgent, setGlobalDispatcher, request } from 'undici'
269
270const mockAgent = new MockAgent()
271setGlobalDispatcher(mockAgent)
272
273const mockPool = mockAgent.get('http://localhost:3000')
274
275mockPool.intercept({
276 path: '/foo',
277 method: /^GET$/,
278 body: (value) => value === 'form=data',
279 headers: {
280 'User-Agent': 'undici',
281 Host: /^example.com$/
282 }
283}).reply(200, 'foo')
284
285const {
286 statusCode,
287 body
288} = await request('http://localhost:3000/foo', {
289 method: 'GET',
290 body: 'form=data',
291 headers: {
292 foo: 'bar',
293 'User-Agent': 'undici',
294 Host: 'example.com'
295 }
296})
297
298console.log('response received', statusCode) // response received 200
299
300for await (const data of body) {
301 console.log('data', data.toString('utf8')) // data foo
302}
303```
304
305#### Example - Mocked request with reply with a defined error
306
307```js
308import { MockAgent, setGlobalDispatcher, request } from 'undici'
309
310const mockAgent = new MockAgent()
311setGlobalDispatcher(mockAgent)
312
313const mockPool = mockAgent.get('http://localhost:3000')
314
315mockPool.intercept({
316 path: '/foo',
317 method: 'GET'
318}).replyWithError(new Error('kaboom'))
319
320try {
321 await request('http://localhost:3000/foo', {
322 method: 'GET'
323 })
324} catch (error) {
325 console.error(error) // Error: kaboom
326}
327```
328
329#### Example - Mocked request with defaultReplyHeaders
330
331```js
332import { MockAgent, setGlobalDispatcher, request } from 'undici'
333
334const mockAgent = new MockAgent()
335setGlobalDispatcher(mockAgent)
336
337const mockPool = mockAgent.get('http://localhost:3000')
338
339mockPool.intercept({
340 path: '/foo',
341 method: 'GET'
342}).defaultReplyHeaders({ foo: 'bar' })
343 .reply(200, 'foo')
344
345const { headers } = await request('http://localhost:3000/foo')
346
347console.log('headers', headers) // headers { foo: 'bar' }
348```
349
350#### Example - Mocked request with defaultReplyTrailers
351
352```js
353import { MockAgent, setGlobalDispatcher, request } from 'undici'
354
355const mockAgent = new MockAgent()
356setGlobalDispatcher(mockAgent)
357
358const mockPool = mockAgent.get('http://localhost:3000')
359
360mockPool.intercept({
361 path: '/foo',
362 method: 'GET'
363}).defaultReplyTrailers({ foo: 'bar' })
364 .reply(200, 'foo')
365
366const { trailers } = await request('http://localhost:3000/foo')
367
368console.log('trailers', trailers) // trailers { foo: 'bar' }
369```
370
371#### Example - Mocked request with automatic content-length calculation
372
373```js
374import { MockAgent, setGlobalDispatcher, request } from 'undici'
375
376const mockAgent = new MockAgent()
377setGlobalDispatcher(mockAgent)
378
379const mockPool = mockAgent.get('http://localhost:3000')
380
381mockPool.intercept({
382 path: '/foo',
383 method: 'GET'
384}).replyContentLength().reply(200, 'foo')
385
386const { headers } = await request('http://localhost:3000/foo')
387
388console.log('headers', headers) // headers { 'content-length': '3' }
389```
390
391#### Example - Mocked request with automatic content-length calculation on an object
392
393```js
394import { MockAgent, setGlobalDispatcher, request } from 'undici'
395
396const mockAgent = new MockAgent()
397setGlobalDispatcher(mockAgent)
398
399const mockPool = mockAgent.get('http://localhost:3000')
400
401mockPool.intercept({
402 path: '/foo',
403 method: 'GET'
404}).replyContentLength().reply(200, { foo: 'bar' })
405
406const { headers } = await request('http://localhost:3000/foo')
407
408console.log('headers', headers) // headers { 'content-length': '13' }
409```
410
411#### Example - Mocked request with persist enabled
412
413```js
414import { MockAgent, setGlobalDispatcher, request } from 'undici'
415
416const mockAgent = new MockAgent()
417setGlobalDispatcher(mockAgent)
418
419const mockPool = mockAgent.get('http://localhost:3000')
420
421mockPool.intercept({
422 path: '/foo',
423 method: 'GET'
424}).reply(200, 'foo').persist()
425
426const result1 = await request('http://localhost:3000/foo')
427// Will match and return mocked data
428
429const result2 = await request('http://localhost:3000/foo')
430// Will match and return mocked data
431
432// Etc
433```
434
435#### Example - Mocked request with times enabled
436
437```js
438import { MockAgent, setGlobalDispatcher, request } from 'undici'
439
440const mockAgent = new MockAgent()
441setGlobalDispatcher(mockAgent)
442
443const mockPool = mockAgent.get('http://localhost:3000')
444
445mockPool.intercept({
446 path: '/foo',
447 method: 'GET'
448}).reply(200, 'foo').times(2)
449
450const result1 = await request('http://localhost:3000/foo')
451// Will match and return mocked data
452
453const result2 = await request('http://localhost:3000/foo')
454// Will match and return mocked data
455
456const result3 = await request('http://localhost:3000/foo')
457// Will not match and make attempt a real request
458```
459
460#### Example - Mocked request with path callback
461
462```js
463import { MockAgent, setGlobalDispatcher, request } from 'undici'
464import querystring from 'querystring'
465
466const mockAgent = new MockAgent()
467setGlobalDispatcher(mockAgent)
468
469const mockPool = mockAgent.get('http://localhost:3000')
470
471const matchPath = requestPath => {
472 const [pathname, search] = requestPath.split('?')
473 const requestQuery = querystring.parse(search)
474
475 if (!pathname.startsWith('/foo')) {
476 return false
477 }
478
479 if (!Object.keys(requestQuery).includes('foo') || requestQuery.foo !== 'bar') {
480 return false
481 }
482
483 return true
484}
485
486mockPool.intercept({
487 path: matchPath,
488 method: 'GET'
489}).reply(200, 'foo')
490
491const result = await request('http://localhost:3000/foo?foo=bar')
492// Will match and return mocked data
493```
494
495### `MockPool.close()`
496
497Closes the mock pool and de-registers from associated MockAgent.
498
499Returns: `Promise<void>`
500
501#### Example - clean up after tests are complete
502
503```js
504import { MockAgent } from 'undici'
505
506const mockAgent = new MockAgent()
507const mockPool = mockAgent.get('http://localhost:3000')
508
509await mockPool.close()
510```
511
512### `MockPool.dispatch(options, handlers)`
513
514Implements [`Dispatcher.dispatch(options, handlers)`](Dispatcher.md#dispatcherdispatchoptions-handler).
515
516### `MockPool.request(options[, callback])`
517
518See [`Dispatcher.request(options [, callback])`](Dispatcher.md#dispatcherrequestoptions-callback).
519
520#### Example - MockPool request
521
522```js
523import { MockAgent } from 'undici'
524
525const mockAgent = new MockAgent()
526
527const mockPool = mockAgent.get('http://localhost:3000')
528mockPool.intercept({
529 path: '/foo',
530 method: 'GET',
531}).reply(200, 'foo')
532
533const {
534 statusCode,
535 body
536} = await mockPool.request({
537 origin: 'http://localhost:3000',
538 path: '/foo',
539 method: 'GET'
540})
541
542console.log('response received', statusCode) // response received 200
543
544for await (const data of body) {
545 console.log('data', data.toString('utf8')) // data foo
546}
547```
Note: See TracBrowser for help on using the repository browser.