1# Class: MockAgent 2 3Extends: `undici.Dispatcher` 4 5A mocked Agent class that implements the Agent API. It allows one to intercept HTTP requests made through undici and return mocked responses instead. 6 7## `new MockAgent([options])` 8 9Arguments: 10 11* **options** `MockAgentOptions` (optional) - It extends the `Agent` options. 12 13Returns: `MockAgent` 14 15### Parameter: `MockAgentOptions` 16 17Extends: [`AgentOptions`](Agent.md#parameter-agentoptions) 18 19* **agent** `Agent` (optional) - Default: `new Agent([options])` - a custom agent encapsulated by the MockAgent. 20 21### Example - Basic MockAgent instantiation 22 23This will instantiate the MockAgent. It will not do anything until registered as the agent to use with requests and mock interceptions are added. 24 25```js 26import { MockAgent } from 'undici' 27 28const mockAgent = new MockAgent() 29``` 30 31### Example - Basic MockAgent instantiation with custom agent 32 33```js 34import { Agent, MockAgent } from 'undici' 35 36const agent = new Agent() 37 38const mockAgent = new MockAgent({ agent }) 39``` 40 41## Instance Methods 42 43### `MockAgent.get(origin)` 44 45This method creates and retrieves MockPool or MockClient instances which can then be used to intercept HTTP requests. If the number of connections on the mock agent is set to 1, a MockClient instance is returned. Otherwise a MockPool instance is returned. 46 47For subsequent `MockAgent.get` calls on the same origin, the same mock instance will be returned. 48 49Arguments: 50 51* **origin** `string | RegExp | (value) => boolean` - a matcher for the pool origin to be retrieved from the MockAgent. 52 53| Matcher type | Condition to pass | 54|:------------:| -------------------------- | 55| `string` | Exact match against string | 56| `RegExp` | Regex must pass | 57| `Function` | Function must return true | 58 59Returns: `MockClient | MockPool`. 60 61| `MockAgentOptions` | Mock instance returned | 62| -------------------- | ---------------------- | 63| `connections === 1` | `MockClient` | 64| `connections` > `1` | `MockPool` | 65 66#### Example - Basic Mocked Request 67 68```js 69import { MockAgent, setGlobalDispatcher, request } from 'undici' 70 71const mockAgent = new MockAgent() 72setGlobalDispatcher(mockAgent) 73 74const mockPool = mockAgent.get('http://localhost:3000') 75mockPool.intercept({ path: '/foo' }).reply(200, 'foo') 76 77const { statusCode, body } = await request('http://localhost:3000/foo') 78 79console.log('response received', statusCode) // response received 200 80 81for await (const data of body) { 82 console.log('data', data.toString('utf8')) // data foo 83} 84``` 85 86#### Example - Basic Mocked Request with local mock agent dispatcher 87 88```js 89import { MockAgent, request } from 'undici' 90 91const mockAgent = new MockAgent() 92 93const mockPool = mockAgent.get('http://localhost:3000') 94mockPool.intercept({ path: '/foo' }).reply(200, 'foo') 95 96const { 97 statusCode, 98 body 99} = await request('http://localhost:3000/foo', { dispatcher: mockAgent }) 100 101console.log('response received', statusCode) // response received 200 102 103for await (const data of body) { 104 console.log('data', data.toString('utf8')) // data foo 105} 106``` 107 108#### Example - Basic Mocked Request with local mock pool dispatcher 109 110```js 111import { MockAgent, request } from 'undici' 112 113const mockAgent = new MockAgent() 114 115const mockPool = mockAgent.get('http://localhost:3000') 116mockPool.intercept({ path: '/foo' }).reply(200, 'foo') 117 118const { 119 statusCode, 120 body 121} = await request('http://localhost:3000/foo', { dispatcher: mockPool }) 122 123console.log('response received', statusCode) // response received 200 124 125for await (const data of body) { 126 console.log('data', data.toString('utf8')) // data foo 127} 128``` 129 130#### Example - Basic Mocked Request with local mock client dispatcher 131 132```js 133import { MockAgent, request } from 'undici' 134 135const mockAgent = new MockAgent({ connections: 1 }) 136 137const mockClient = mockAgent.get('http://localhost:3000') 138mockClient.intercept({ path: '/foo' }).reply(200, 'foo') 139 140const { 141 statusCode, 142 body 143} = await request('http://localhost:3000/foo', { dispatcher: mockClient }) 144 145console.log('response received', statusCode) // response received 200 146 147for await (const data of body) { 148 console.log('data', data.toString('utf8')) // data foo 149} 150``` 151 152#### Example - Basic Mocked requests with multiple intercepts 153 154```js 155import { MockAgent, setGlobalDispatcher, request } from 'undici' 156 157const mockAgent = new MockAgent() 158setGlobalDispatcher(mockAgent) 159 160const mockPool = mockAgent.get('http://localhost:3000') 161mockPool.intercept({ path: '/foo' }).reply(200, 'foo') 162mockPool.intercept({ path: '/hello'}).reply(200, 'hello') 163 164const result1 = await request('http://localhost:3000/foo') 165 166console.log('response received', result1.statusCode) // response received 200 167 168for await (const data of result1.body) { 169 console.log('data', data.toString('utf8')) // data foo 170} 171 172const result2 = await request('http://localhost:3000/hello') 173 174console.log('response received', result2.statusCode) // response received 200 175 176for await (const data of result2.body) { 177 console.log('data', data.toString('utf8')) // data hello 178} 179``` 180#### Example - Mock different requests within the same file 181```js 182const { MockAgent, setGlobalDispatcher } = require('undici'); 183const agent = new MockAgent(); 184agent.disableNetConnect(); 185setGlobalDispatcher(agent); 186describe('Test', () => { 187 it('200', async () => { 188 const mockAgent = agent.get('http://test.com'); 189 // your test 190 }); 191 it('200', async () => { 192 const mockAgent = agent.get('http://testing.com'); 193 // your test 194 }); 195}); 196``` 197 198#### Example - Mocked request with query body, headers and trailers 199 200```js 201import { MockAgent, setGlobalDispatcher, request } from 'undici' 202 203const mockAgent = new MockAgent() 204setGlobalDispatcher(mockAgent) 205 206const mockPool = mockAgent.get('http://localhost:3000') 207 208mockPool.intercept({ 209 path: '/foo?hello=there&see=ya', 210 method: 'POST', 211 body: 'form1=data1&form2=data2' 212}).reply(200, { foo: 'bar' }, { 213 headers: { 'content-type': 'application/json' }, 214 trailers: { 'Content-MD5': 'test' } 215}) 216 217const { 218 statusCode, 219 headers, 220 trailers, 221 body 222} = await request('http://localhost:3000/foo?hello=there&see=ya', { 223 method: 'POST', 224 body: 'form1=data1&form2=data2' 225}) 226 227console.log('response received', statusCode) // response received 200 228console.log('headers', headers) // { 'content-type': 'application/json' } 229 230for await (const data of body) { 231 console.log('data', data.toString('utf8')) // '{"foo":"bar"}' 232} 233 234console.log('trailers', trailers) // { 'content-md5': 'test' } 235``` 236 237#### Example - Mocked request with origin regex 238 239```js 240import { MockAgent, setGlobalDispatcher, request } from 'undici' 241 242const mockAgent = new MockAgent() 243setGlobalDispatcher(mockAgent) 244 245const mockPool = mockAgent.get(new RegExp('http://localhost:3000')) 246mockPool.intercept({ path: '/foo' }).reply(200, 'foo') 247 248const { 249 statusCode, 250 body 251} = await request('http://localhost:3000/foo') 252 253console.log('response received', statusCode) // response received 200 254 255for await (const data of body) { 256 console.log('data', data.toString('utf8')) // data foo 257} 258``` 259 260#### Example - Mocked request with origin function 261 262```js 263import { MockAgent, setGlobalDispatcher, request } from 'undici' 264 265const mockAgent = new MockAgent() 266setGlobalDispatcher(mockAgent) 267 268const mockPool = mockAgent.get((origin) => origin === 'http://localhost:3000') 269mockPool.intercept({ path: '/foo' }).reply(200, 'foo') 270 271const { 272 statusCode, 273 body 274} = await request('http://localhost:3000/foo') 275 276console.log('response received', statusCode) // response received 200 277 278for await (const data of body) { 279 console.log('data', data.toString('utf8')) // data foo 280} 281``` 282 283### `MockAgent.close()` 284 285Closes the mock agent and waits for registered mock pools and clients to also close before resolving. 286 287Returns: `Promise<void>` 288 289#### Example - clean up after tests are complete 290 291```js 292import { MockAgent, setGlobalDispatcher } from 'undici' 293 294const mockAgent = new MockAgent() 295setGlobalDispatcher(mockAgent) 296 297await mockAgent.close() 298``` 299 300### `MockAgent.dispatch(options, handlers)` 301 302Implements [`Agent.dispatch(options, handlers)`](Agent.md#parameter-agentdispatchoptions). 303 304### `MockAgent.request(options[, callback])` 305 306See [`Dispatcher.request(options [, callback])`](Dispatcher.md#dispatcherrequestoptions-callback). 307 308#### Example - MockAgent request 309 310```js 311import { MockAgent } from 'undici' 312 313const mockAgent = new MockAgent() 314 315const mockPool = mockAgent.get('http://localhost:3000') 316mockPool.intercept({ path: '/foo' }).reply(200, 'foo') 317 318const { 319 statusCode, 320 body 321} = await mockAgent.request({ 322 origin: 'http://localhost:3000', 323 path: '/foo', 324 method: 'GET' 325}) 326 327console.log('response received', statusCode) // response received 200 328 329for await (const data of body) { 330 console.log('data', data.toString('utf8')) // data foo 331} 332``` 333 334### `MockAgent.deactivate()` 335 336This method disables mocking in MockAgent. 337 338Returns: `void` 339 340#### Example - Deactivate Mocking 341 342```js 343import { MockAgent, setGlobalDispatcher } from 'undici' 344 345const mockAgent = new MockAgent() 346setGlobalDispatcher(mockAgent) 347 348mockAgent.deactivate() 349``` 350 351### `MockAgent.activate()` 352 353This method enables mocking in a MockAgent instance. When instantiated, a MockAgent is automatically activated. Therefore, this method is only effective after `MockAgent.deactivate` has been called. 354 355Returns: `void` 356 357#### Example - Activate Mocking 358 359```js 360import { MockAgent, setGlobalDispatcher } from 'undici' 361 362const mockAgent = new MockAgent() 363setGlobalDispatcher(mockAgent) 364 365mockAgent.deactivate() 366// No mocking will occur 367 368// Later 369mockAgent.activate() 370``` 371 372### `MockAgent.enableNetConnect([host])` 373 374When requests are not matched in a MockAgent intercept, a real HTTP request is attempted. We can control this further through the use of `enableNetConnect`. This is achieved by defining host matchers so only matching requests will be attempted. 375 376When using a string, it should only include the **hostname and optionally, the port**. In addition, calling this method multiple times with a string will allow all HTTP requests that match these values. 377 378Arguments: 379 380* **host** `string | RegExp | (value) => boolean` - (optional) 381 382Returns: `void` 383 384#### Example - Allow all non-matching urls to be dispatched in a real HTTP request 385 386```js 387import { MockAgent, setGlobalDispatcher, request } from 'undici' 388 389const mockAgent = new MockAgent() 390setGlobalDispatcher(mockAgent) 391 392mockAgent.enableNetConnect() 393 394await request('http://example.com') 395// A real request is made 396``` 397 398#### Example - Allow requests matching a host string to make real requests 399 400```js 401import { MockAgent, setGlobalDispatcher, request } from 'undici' 402 403const mockAgent = new MockAgent() 404setGlobalDispatcher(mockAgent) 405 406mockAgent.enableNetConnect('example-1.com') 407mockAgent.enableNetConnect('example-2.com:8080') 408 409await request('http://example-1.com') 410// A real request is made 411 412await request('http://example-2.com:8080') 413// A real request is made 414 415await request('http://example-3.com') 416// Will throw 417``` 418 419#### Example - Allow requests matching a host regex to make real requests 420 421```js 422import { MockAgent, setGlobalDispatcher, request } from 'undici' 423 424const mockAgent = new MockAgent() 425setGlobalDispatcher(mockAgent) 426 427mockAgent.enableNetConnect(new RegExp('example.com')) 428 429await request('http://example.com') 430// A real request is made 431``` 432 433#### Example - Allow requests matching a host function to make real requests 434 435```js 436import { MockAgent, setGlobalDispatcher, request } from 'undici' 437 438const mockAgent = new MockAgent() 439setGlobalDispatcher(mockAgent) 440 441mockAgent.enableNetConnect((value) => value === 'example.com') 442 443await request('http://example.com') 444// A real request is made 445``` 446 447### `MockAgent.disableNetConnect()` 448 449This method causes all requests to throw when requests are not matched in a MockAgent intercept. 450 451Returns: `void` 452 453#### Example - Disable all non-matching requests by throwing an error for each 454 455```js 456import { MockAgent, request } from 'undici' 457 458const mockAgent = new MockAgent() 459 460mockAgent.disableNetConnect() 461 462await request('http://example.com') 463// Will throw 464``` 465 466### `MockAgent.pendingInterceptors()` 467 468This method returns any pending interceptors registered on a mock agent. A pending interceptor meets one of the following criteria: 469 470- Is registered with neither `.times(<number>)` nor `.persist()`, and has not been invoked; 471- Is persistent (i.e., registered with `.persist()`) and has not been invoked; 472- Is registered with `.times(<number>)` and has not been invoked `<number>` of times. 473 474Returns: `PendingInterceptor[]` (where `PendingInterceptor` is a `MockDispatch` with an additional `origin: string`) 475 476#### Example - List all pending inteceptors 477 478```js 479const agent = new MockAgent() 480agent.disableNetConnect() 481 482agent 483 .get('https://example.com') 484 .intercept({ method: 'GET', path: '/' }) 485 .reply(200) 486 487const pendingInterceptors = agent.pendingInterceptors() 488// Returns [ 489// { 490// timesInvoked: 0, 491// times: 1, 492// persist: false, 493// consumed: false, 494// pending: true, 495// path: '/', 496// method: 'GET', 497// body: undefined, 498// headers: undefined, 499// data: { 500// error: null, 501// statusCode: 200, 502// data: '', 503// headers: {}, 504// trailers: {} 505// }, 506// origin: 'https://example.com' 507// } 508// ] 509``` 510 511### `MockAgent.assertNoPendingInterceptors([options])` 512 513This method throws if the mock agent has any pending interceptors. A pending interceptor meets one of the following criteria: 514 515- Is registered with neither `.times(<number>)` nor `.persist()`, and has not been invoked; 516- Is persistent (i.e., registered with `.persist()`) and has not been invoked; 517- Is registered with `.times(<number>)` and has not been invoked `<number>` of times. 518 519#### Example - Check that there are no pending interceptors 520 521```js 522const agent = new MockAgent() 523agent.disableNetConnect() 524 525agent 526 .get('https://example.com') 527 .intercept({ method: 'GET', path: '/' }) 528 .reply(200) 529 530agent.assertNoPendingInterceptors() 531// Throws an UndiciError with the following message: 532// 533// 1 interceptor is pending: 534// 535// ┌─────────┬────────┬───────────────────────┬──────┬─────────────┬────────────┬─────────────┬───────────┐ 536// │ (index) │ Method │ Origin │ Path │ Status code │ Persistent │ Invocations │ Remaining │ 537// ├─────────┼────────┼───────────────────────┼──────┼─────────────┼────────────┼─────────────┼───────────┤ 538// │ 0 │ 'GET' │ 'https://example.com' │ '/' │ 200 │ '❌' │ 0 │ 1 │ 539// └─────────┴────────┴───────────────────────┴──────┴─────────────┴────────────┴─────────────┴───────────┘ 540``` 541