UNPKG

40.3 kBMarkdownView Raw
1# Jest Fetch Mock
2
3Fetch is the canonical way to do HTTP requests in the browser, and it can be used in other environments such as React Native. Jest Fetch Mock allows you to easily mock your `fetch` calls and return the response you need to fake the HTTP requests. It's easy to setup and you don't need a library like `nock` to get going and it uses Jest's built-in support for mocking under the surface. This means that any of the `jest.fn()` methods are also available. For more information on the jest mock API, check their docs [here](https://facebook.github.io/jest/docs/en/mock-functions.html)
4
5It currently supports the mocking with the [`cross-fetch`](https://www.npmjs.com/package/cross-fetch) polyfill, so it supports Node.js and any browser-like runtime.
6
7## Contents
8
9- [Usage](#usage)
10 - [Installation and Setup](#installation-and-setup)
11 - [Using with Create-React-App](#using-with-create-react-app)
12- [API](#api)
13- [Examples](#examples)
14 - [Simple mock and assert](#simple-mock-and-assert)
15 - [Mocking all fetches](#mocking-all-fetches)
16 - [Mocking a failed fetch](#mocking-a-failed-fetch)
17 - [Mocking multiple fetches with different responses](#mocking-multiple-fetches-with-different-responses)
18 - [Mocking multiple fetches with `fetch.mockResponses`](#mocking-multiple-fetches-with-fetchmockresponses)
19 - [Reset mocks between tests with `fetch.resetMocks`](#reset-mocks-between-tests-with-fetchresetmocks)
20 - [Using `fetch.mock` to inspect the mock state of each fetch call](#using-fetchmock-to-inspect-the-mock-state-of-each-fetch-call)
21 - [Using functions to mock slow servers](#using-functions-to-mock-slow-servers)
22
23## Usage
24
25### Package Installation
26
27To setup your fetch mock you need to do the following things:
28
29```
30$ npm install --save-dev jest-fetch-mock
31```
32
33Create a `setupJest` file to setup the mock or add this to an existing `setupFile`. :
34
35### To setup for all tests
36
37```js
38//setupJest.js or similar file
39require('jest-fetch-mock').enableMocks()
40```
41
42Add the setupFile to your jest config in `package.json`:
43
44```JSON
45"jest": {
46 "automock": false,
47 "setupFiles": [
48 "./setupJest.js"
49 ]
50}
51```
52
53With this done, you'll have `fetch` and `fetchMock` available on the global scope. Fetch will be used as usual by your code and you'll use `fetchMock` in your tests
54
55#### Default not mocked
56
57If you would like to have the 'fetchMock' available in all tests but not enabled then add `fetchMock.dontMock()` after the `...enableMocks()` line in `setupJest.js`:
58```js
59// adds the 'fetchMock' global variable and rewires 'fetch' global to call 'fetchMock' instead of the real implementation
60require('jest-fetch-mock').enableMocks()
61// changes default behavior of fetchMock to use the real 'fetch' implementation and not mock responses
62fetchMock.dontMock()
63```
64If you want a single test file to return to the default behavior of mocking all responses, add the following to the
65test file:
66```js
67beforeEach(() => { // if you have an existing `beforeEach` just add the following line to it
68 fetchMock.doMock()
69})
70```
71
72To enable mocking for a specific URL only:
73```js
74beforeEach(() => { // if you have an existing `beforeEach` just add the following lines to it
75 fetchMock.mockIf(/^https?:\/\/example.com.*$/, req => {
76 if (req.url.endsWith("/path1")) {
77 return "some response body"
78 } else if (req.url.endsWith("/path2")) {
79 return {
80 body: "another response body",
81 headers: {
82 "X-Some-Response-Header": "Some header value"
83 }
84 }
85 } else {
86 return {
87 status: 404,
88 body: "Not Found"
89 }
90 }
91 })
92})
93```
94
95If you have changed the default behavior to use the real implementation, you can guarantee the next call to fetch
96will be mocked by using the `mockOnce` function:
97```js
98fetchMock.mockOnce("the next call to fetch will always return this as the body")
99```
100
101This function behaves exactly like `fetchMock.once` but guarantees the next call to `fetch` will be mocked even if the
102default behavior of fetchMock is to use the real implementation. You can safely convert all you `fetchMock.once` calls
103to `fetchMock.mockOnce` without a risk of changing their behavior.
104
105### To setup for an individual test
106
107For JavaScript add the following line to the start of your test case (before any other requires)
108```js
109require('jest-fetch-mock').enableMocks()
110```
111
112For TypeScript/ES6 add the following lines to the start of your test case (before any other imports)
113```typescript
114import { enableFetchMocks } from 'jest-fetch-mock'
115enableFetchMocks()
116```
117
118#### TypeScript importing
119
120If you are using TypeScript and receive errors about the `fetchMock` global not existing,
121add a `global.d.ts` file to the root of your project (or add the following line to an existing global file):
122
123```typescript
124import 'jest-fetch-mock'
125```
126
127If you prefer you can also just import the fetchMock in a test case.
128```typescript
129import fetchMock from "jest-fetch-mock"
130```
131
132You may also need to edit your `tsconfig.json` and add "dom" and/or "es2015" and/or "esnext" to the 'compilerConfig.lib' property
133
134### Using with Create-React-App
135
136If you are using [Create-React-App](https://github.com/facebookincubator/create-react-app) (CRA), the code for `setupTest.js` above should be placed into `src/setupTests.js` in the root of your project. CRA automatically uses this filename by convention in the Jest configuration it generates. Similarly, changing to your `package.json` is not required as CRA handles this when generating your Jest configuration.
137
138### For Ejected Create React Apps _ONLY_:
139
140> Note: Keep in mind that if you decide to "eject" before creating src/setupTests.js, the resulting package.json file won't contain any reference to it, so you should manually create the property setupTestFrameworkScriptFile in the configuration for Jest, something like the [following](https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#srcsetuptestsjs-1):
141
142```JSON
143"jest": {
144 "setupTestFrameworkScriptFile": "<rootDir>/src/setupTests.js"
145 }
146```
147
148## API
149
150### Mock Responses
151
152- `fetch.mockResponse(bodyOrFunction, init): fetch` - Mock all fetch calls
153- `fetch.mockResponseOnce(bodyOrFunction, init): fetch` - Mock each fetch call independently
154- `fetch.once(bodyOrFunction, init): fetch` - Alias for `mockResponseOnce(bodyOrFunction, init)`
155- `fetch.mockResponses(...responses): fetch` - Mock multiple fetch calls independently
156 - Each argument is an array taking `[bodyOrFunction, init]`
157- `fetch.mockReject(errorOrFunction): fetch` - Mock all fetch calls, letting them fail directly
158- `fetch.mockRejectOnce(errorOrFunction): fetch` - Let the next fetch call fail directly
159- `fetch.mockAbort(): fetch` - Causes all fetch calls to reject with an "Aborted!" error
160- `fetch.mockAbortOnce(): fetch` - Causes the next fetch call to reject with an "Aborted!" error
161
162### Functions
163
164Instead of passing body, it is also possible to pass a function that returns a promise.
165The promise should resolve with a string or an object containing body and init props
166
167i.e:
168
169```js
170fetch.mockResponse(() => callMyApi().then(res => ({ body: 'ok' })))
171// OR
172fetch.mockResponse(() => callMyApi().then(res => 'ok'))
173```
174
175The function may take an optional "request" parameter of type `http.Request`:
176
177```js
178fetch.mockResponse(req =>
179 req.url === 'http://myapi/'
180 ? callMyApi().then(res => 'ok')
181 : Promise.reject(new Error('bad url'))
182)
183```
184
185Note: the request "url" is parsed and then printed using the equivalent of `new URL(input).href` so it may not match exactly with the URL's passed to `fetch` if they are not fully qualified.
186For example, passing "http://foo.com" to `fetch` will result in the request URL being "http://foo.com/" (note the trailing slash).
187
188The same goes for rejects:
189
190```js
191fetch.mockReject(() =>
192 doMyAsyncJob().then(res => Promise.reject(res.errorToRaise))
193)
194// OR
195fetch.mockReject(req =>
196 req.headers.get('content-type') === 'text/plain'
197 ? Promise.reject('invalid content type')
198 : doMyAsyncJob().then(res => Promise.reject(res.errorToRaise))
199)
200```
201
202### Mock utilities
203
204- `fetch.resetMocks()` - Clear previously set mocks so they do not bleed into other mocks
205- `fetch.enableMocks()` - Enable fetch mocking by overriding `global.fetch` and mocking `node-fetch`
206- `fetch.disableMocks()` - Disable fetch mocking and restore default implementation of `fetch` and/or `node-fetch`
207- `fetch.mock` - The mock state for your fetch calls. Make assertions on the arguments given to `fetch` when called by the functions you are testing. For more information check the [Jest docs](https://facebook.github.io/jest/docs/en/mock-functions.html#mock-property)
208
209For information on the arguments body and init can take, you can look at the MDN docs on the Response Constructor function, which `jest-fetch-mock` uses under the surface.
210
211https://developer.mozilla.org/en-US/docs/Web/API/Response/Response
212
213Each mocked response or err
214or will return a [Mock Function](http://facebook.github.io/jest/docs/mock-function-api.html#content). You can use methods like `.toHaveBeenCalledWith` to ensure that the mock function was called with specific arguments. For more methods detail, take a look at [this](http://facebook.github.io/jest/docs/expect.html#content).
215
216## Examples
217
218In most of the complicated examples below, I am testing my action creators in Redux, but it doesn't have to be used with Redux.
219
220### Simple mock and assert
221
222In this simple example I won't be using any libraries. It is a simple fetch request, in this case to google.com. First we setup the `beforeEach` callback to reset our mocks. This isn't strictly necessary in this example, but since we will probably be mocking fetch more than once, we need to reset it across our tests to assert on the arguments given to fetch.
223
224Once we've done that we can start to mock our response. We want to give it an objectwith a `data` property and a string value of `12345` and wrap it in `JSON.stringify` to JSONify it. Here we use `mockResponseOnce`, but we could also use `once`, which is an alias for a call to `mockResponseOnce`
225
226We then call the function that we want to test with the arguments we want to test with. In the `then` callback we assert we have got the correct data back.
227
228Finally we can assert on the `.mock` state that Jest provides for us to test what arguments were given to fetch and how many times it was called
229
230```js
231//api.js
232export function APIRequest(who) {
233 if (who === 'google') {
234 return fetch('https://google.com').then(res => res.json())
235 } else {
236 return 'no argument provided'
237 }
238}
239```
240
241```js
242//api.test.js
243import { APIRequest } from './api'
244
245describe('testing api', () => {
246 beforeEach(() => {
247 fetch.resetMocks()
248 })
249
250 it('calls google and returns data to me', () => {
251 fetch.mockResponseOnce(JSON.stringify({ data: '12345' }))
252
253 //assert on the response
254 APIRequest('google').then(res => {
255 expect(res.data).toEqual('12345')
256 })
257
258 //assert on the times called and arguments given to fetch
259 expect(fetch.mock.calls.length).toEqual(1)
260 expect(fetch.mock.calls[0][0]).toEqual('https://google.com')
261 })
262})
263```
264
265### Mocking all fetches
266
267In this example I am mocking just one fetch call. Any additional fetch calls in the same function will also have the same mock response. For more complicated functions with multiple fetch calls, you can check out example 3.
268
269```js
270import configureMockStore from 'redux-mock-store' // mock store
271import thunk from 'redux-thunk'
272
273const middlewares = [thunk]
274const mockStore = configureMockStore(middlewares)
275
276import { getAccessToken } from './accessToken'
277
278describe('Access token action creators', () => {
279 it('dispatches the correct actions on successful fetch request', () => {
280 fetch.mockResponse(JSON.stringify({ access_token: '12345' }))
281
282 const expectedActions = [
283 { type: 'SET_ACCESS_TOKEN', token: { access_token: '12345' } }
284 ]
285 const store = mockStore({ config: { token: '' } })
286
287 return (
288 store
289 .dispatch(getAccessToken())
290 //getAccessToken contains the fetch call
291 .then(() => {
292 // return of async actions
293 expect(store.getActions()).toEqual(expectedActions)
294 })
295 )
296 })
297})
298```
299
300### Mocking a failed fetch
301
302In this example I am mocking just one fetch call but this time using the `mockReject` function to simulate a failed request. Any additional fetch calls in the same function will also have the same mock response. For more complicated functions with multiple fetch calls, you can check out example 3.
303
304```js
305import configureMockStore from 'redux-mock-store' // mock store
306import thunk from 'redux-thunk'
307
308const middlewares = [thunk]
309const mockStore = configureMockStore(middlewares)
310
311import { getAccessToken } from './accessToken'
312
313describe('Access token action creators', () => {
314 it('dispatches the correct actions on a failed fetch request', () => {
315 fetch.mockReject(new Error('fake error message'))
316
317 const expectedActions = [
318 { type: 'SET_ACCESS_TOKEN_FAILED', error: { status: 503 } }
319 ]
320 const store = mockStore({ config: { token: '' } })
321
322 return (
323 store
324 .dispatch(getAccessToken())
325 //getAccessToken contains the fetch call
326 .then(() => {
327 // return of async actions
328 expect(store.getActions()).toEqual(expectedActions)
329 })
330 )
331 })
332})
333```
334
335### Mocking aborted fetches
336
337Fetches can be mocked to act as if they were aborted during the request. This can be done in 4 ways:
338<ol>
339<li>Using `fetch.mockAborted()`</li>
340<li>Using `fetch.mockAbortedOnce()`</li>
341<li>Passing a request (or request init) with a 'signal' to fetch that has been aborted</li>
342<li>Passing a request (or request init) with a 'signal' to fetch and a async function to `fetch.mockResponse` or `fetch.mockResponseOnce` that causes the signal to abort before returning the response</li>
343</ol>
344
345```js
346describe('Mocking aborts', () => {
347 beforeEach(() => {
348 fetch.resetMocks()
349 fetch.doMock()
350 jest.useFakeTimers()
351 })
352 afterEach(() => {
353 jest.useRealTimers()
354 })
355
356 it('rejects with an Aborted! Error', () => {
357 fetch.mockAbort()
358 expect(fetch('/')).rejects.toThrow('Aborted!')
359 })
360 it('rejects with an Aborted! Error once then mocks with empty response', async () => {
361 fetch.mockAbortOnce()
362 await expect(fetch('/')).rejects.toThrow('Aborted!')
363 await expect(request()).resolves.toEqual('')
364 })
365
366 it('throws when passed an already aborted abort signal', () => {
367 const c = new AbortController()
368 c.abort()
369 expect(() => fetch('/', { signal: c.signal })).toThrow('Aborted!')
370 })
371
372 it('rejects when aborted before resolved', async () => {
373 const c = new AbortController()
374 fetch.mockResponse(async () => {
375 jest.advanceTimersByTime(60)
376 return ''
377 })
378 setTimeout(() => c.abort(), 50)
379 await expect(fetch('/', { signal: c.signal })).rejects.toThrow('Aborted!')
380 })
381})
382```
383
384### Mocking multiple fetches with different responses
385
386In this next example, the store does not yet have a token, so we make a request to get an access token first. This means that we need to mock two different responses, one for each of the fetches. Here we can use `fetch.mockResponseOnce` or `fetch.once` to mock the response only once and call it twice. Because we return the mocked function, we can chain this jQuery style. It internally uses Jest's `mockImplementationOnce`. You can read more about it on the [Jest documentation](https://facebook.github.io/jest/docs/mock-functions.html#content)
387
388```js
389import configureMockStore from 'redux-mock-store'
390import thunk from 'redux-thunk'
391
392const middlewares = [thunk]
393const mockStore = configureMockStore(middlewares)
394
395import { getAnimeDetails } from './animeDetails'
396
397describe('Anime details action creators', () => {
398 it('dispatches requests for an access token before requesting for animeDetails', () => {
399 fetch
400 .once(JSON.stringify({ access_token: '12345' }))
401 .once(JSON.stringify({ name: 'naruto' }))
402
403 const expectedActions = [
404 { type: 'SET_ACCESS_TOKEN', token: { access_token: '12345' } },
405 { type: 'SET_ANIME_DETAILS', animeDetails: { name: 'naruto' } }
406 ]
407 const store = mockStore({ config: { token: null } })
408
409 return (
410 store
411 .dispatch(getAnimeDetails('21049'))
412 //getAnimeDetails contains the 2 fetch calls
413 .then(() => {
414 // return of async actions
415 expect(store.getActions()).toEqual(expectedActions)
416 })
417 )
418 })
419})
420```
421
422### Mocking multiple fetches with `fetch.mockResponses`
423
424`fetch.mockResponses` takes as many arguments as you give it, all of which are arrays representing each Response Object. It will then call the `mockImplementationOnce` for each response object you give it. This reduces the amount of boilerplate code you need to write. An alternative is to use `.once` and chain it multiple times if you don't like wrapping each response arguments in a tuple/array.
425
426In this example our actionCreator calls `fetch` 4 times, once for each season of the year and then concatenates the results into one final array. You'd have to write `fetch.mockResponseOnce` 4 times to achieve the same thing:
427
428```js
429describe('getYear action creator', () => {
430 it('dispatches the correct actions on successful getSeason fetch request', () => {
431 fetch.mockResponses(
432 [
433 JSON.stringify([{ name: 'naruto', average_score: 79 }]),
434 { status: 200 }
435 ],
436 [
437 JSON.stringify([{ name: 'bleach', average_score: 68 }]),
438 { status: 200 }
439 ],
440 [
441 JSON.stringify([{ name: 'one piece', average_score: 80 }]),
442 { status: 200 }
443 ],
444 [
445 JSON.stringify([{ name: 'shingeki', average_score: 91 }]),
446 { status: 200 }
447 ]
448 )
449
450 const expectedActions = [
451 {
452 type: 'FETCH_ANIMELIST_REQUEST'
453 },
454 {
455 type: 'SET_YEAR',
456 payload: {
457 animes: [
458 { name: 'naruto', average_score: 7.9 },
459 { name: 'bleach', average_score: 6.8 },
460 { name: 'one piece', average_score: 8 },
461 { name: 'shingeki', average_score: 9.1 }
462 ],
463 year: 2016
464 }
465 },
466 {
467 type: 'FETCH_ANIMELIST_COMPLETE'
468 }
469 ]
470 const store = mockStore({
471 config: {
472 token: { access_token: 'wtw45CmyEuh4P621IDVxWkgVr5QwTg3wXEc4Z7Cv' }
473 },
474 years: []
475 })
476
477 return (
478 store
479 .dispatch(getYear(2016))
480 //This calls fetch 4 times, once for each season
481 .then(() => {
482 // return of async actions
483 expect(store.getActions()).toEqual(expectedActions)
484 })
485 )
486 })
487})
488```
489
490### Reset mocks between tests with `fetch.resetMocks`
491
492`fetch.resetMocks` resets the `fetch` mock to give fresh mock data in between tests. It only resets the `fetch` calls as to not disturb any other mocked functionality.
493
494```js
495describe('getYear action creator', () => {
496 beforeEach(() => {
497 fetch.resetMocks();
498 });
499 it('dispatches the correct actions on successful getSeason fetch request', () => {
500
501 fetch.mockResponses(
502 [
503 JSON.stringify([ {name: 'naruto', average_score: 79} ]), { status: 200}
504 ],
505 [
506 JSON.stringify([ {name: 'bleach', average_score: 68} ]), { status: 200}
507 ]
508 )
509
510 const expectedActions = [
511 {
512 type: 'FETCH_ANIMELIST_REQUEST'
513 },
514 {
515 type: 'SET_YEAR',
516 payload: {
517 animes: [
518 {name: 'naruto', average_score: 7.9},
519 {name: 'bleach', average_score: 6.8}
520 ],
521 year: 2016,
522 }
523 },
524 {
525 type: 'FETCH_ANIMELIST_COMPLETE'
526 }
527 ]
528 const store = mockStore({
529 config: { token: { access_token: 'wtw45CmyEuh4P621IDVxWkgVr5QwTg3wXEc4Z7Cv' }},
530 years: []
531 })
532
533 return store.dispatch(getYear(2016))
534 //This calls fetch 2 times, once for each season
535 .then(() => { // return of async actions
536 expect(store.getActions()).toEqual(expectedActions)
537 })
538 });
539 it('dispatches the correct actions on successful getSeason fetch request', () => {
540
541 fetch.mockResponses(
542 [
543 JSON.stringify([ {name: 'bleach', average_score: 68} ]), { status: 200}
544 ],
545 [
546 JSON.stringify([ {name: 'one piece', average_score: 80} ]), { status: 200}
547 ],
548 [
549 JSON.stringify([ {name: 'shingeki', average_score: 91} ]), { status: 200}
550 ]
551 )
552
553 const expectedActions = [
554 {
555 type: 'FETCH_ANIMELIST_REQUEST'
556 },
557 {
558 type: 'SET_YEAR',
559 payload: {
560 animes: [
561 {name: 'bleach', average_score: 6.8},
562 {name: 'one piece', average_score: 8},
563 {name: 'shingeki', average_score: 9.1}
564 ],
565 year: 2016,
566 }
567 },
568 {
569 type: 'FETCH_ANIMELIST_COMPLETE'
570 }
571 ]
572 const store = mockStore({
573 config: { token: { access_token: 'wtw45CmyEuh4P621IDVxWkgVr5QwTg3wXEc4Z7Cv' }},
574 years: []
575 })
576
577 return store.dispatch(getYear(2016))
578 //This calls fetch 3 times, once for each season
579 .then(() => { // return of async actions
580 expect(store.getActions()).toEqual(expectedActions)
581 })
582 ,
583
584 })
585})
586```
587
588### Using `fetch.mock` to inspect the mock state of each fetch call
589
590`fetch.mock` by default uses [Jest's mocking functions](https://facebook.github.io/jest/docs/en/mock-functions.html#mock-property). Therefore you can make assertions on the mock state. In this example we have an arbitrary function that makes a different fetch request based on the argument you pass to it. In our test, we run Jest's `beforeEach()` and make sure to reset our mock before each `it()` block as we will make assertions on the arguments we are passing to `fetch()`. The most uses property is the `fetch.mock.calls` array. It can give you information on each call, and their arguments which you can use for your `expect()` calls. Jest also comes with some nice aliases for the most used ones.
591
592```js
593// api.js
594
595import 'cross-fetch'
596
597export function APIRequest(who) {
598 if (who === 'facebook') {
599 return fetch('https://facebook.com')
600 } else if (who === 'twitter') {
601 return fetch('https://twitter.com')
602 } else {
603 return fetch('https://google.com')
604 }
605}
606```
607
608```js
609// api.test.js
610import { APIRequest } from './api'
611
612describe('testing api', () => {
613 beforeEach(() => {
614 fetch.resetMocks()
615 })
616
617 it('calls google by default', () => {
618 fetch.mockResponse(JSON.stringify({ secret_data: '12345' }))
619 APIRequest()
620
621 expect(fetch.mock.calls.length).toEqual(1)
622 expect(fetch.mock.calls[0][0]).toEqual('https://google.com')
623 })
624
625 it('calls facebook', () => {
626 fetch.mockResponse(JSON.stringify({ secret_data: '12345' }))
627 APIRequest('facebook')
628
629 expect(fetch.mock.calls.length).toEqual(2)
630 expect(fetch.mock.calls[0][0]).toEqual(
631 'https://facebook.com/someOtherResource'
632 )
633 expect(fetch.mock.calls[1][0]).toEqual('https://facebook.com')
634 })
635
636 it('calls twitter', () => {
637 fetch.mockResponse(JSON.stringify({ secret_data: '12345' }))
638 APIRequest('twitter')
639
640 expect(fetch).toBeCalled() // alias for expect(fetch.mock.calls.length).toEqual(1);
641 expect(fetch).toBeCalledWith('https://twitter.com') // alias for expect(fetch.mock.calls[0][0]).toEqual();
642 })
643})
644```
645
646### Using functions to mock slow servers
647
648By default you will want to have your fetch mock return immediately. However if you have some custom logic that needs to tests for slower servers, you can do this by passing it a function and returning a promise when your function resolves
649
650```js
651// api.test.js
652import { request } from './api'
653
654describe('testing timeouts', () => {
655 it('resolves with function and timeout', async () => {
656 fetch.mockResponseOnce(
657 () =>
658 new Promise(resolve => setTimeout(() => resolve({ body: 'ok' }), 100))
659 )
660 try {
661 const response = await request()
662 expect(response).toEqual('ok')
663 } catch (e) {
664 throw e
665 }
666 })
667})
668```
669
670### Conditional Mocking
671
672In some test scenarios, you may want to temporarily disable (or enable) mocking for all requests or the next (or a certain number of) request(s).
673You may want to only mock fetch requests to some URLs that match a given request path while in others you may want to mock
674all requests except those matching a given request path. You may even want to conditionally mock based on request headers.
675
676The conditional mock functions cause `jest-fetch-mock` to pass fetches through to the concrete fetch implementation conditionally.
677Calling `fetch.dontMock`, `fetch.doMock`, `fetch.doMockIf` or `fetch.dontMockIf` overrides the default behavior
678of mocking/not mocking all requests. `fetch.dontMockOnce`, `fetch.doMockOnce`, `fetch.doMockOnceIf` and `fetch.dontMockOnceIf` only overrides the behavior
679for the next call to `fetch`, then returns to the default behavior (either mocking all requests or mocking the requests based on the last call to
680`fetch.dontMock`, `fetch.doMock`, `fetch.doMockIf` and `fetch.dontMockIf`).
681
682Calling `fetch.resetMocks()` will return to the default behavior of mocking all fetches with a text response of empty string.
683
684- `fetch.dontMock()` - Change the default behavior to not mock any fetches until `fetch.resetMocks()` or `fetch.doMock()` is called
685- `fetch.doMock(bodyOrFunction?, responseInit?)` - Reverses `fetch.dontMock()`. This is the default state after `fetch.resetMocks()`
686- `fetch.dontMockOnce()` - For the next fetch, do not mock then return to the default behavior for subsequent fetches. Can be chained.
687- `fetch.doMockOnce(bodyOrFunction?, responseInit?)` or `fetch.mockOnce` - For the next fetch, mock the response then return to the default behavior for subsequent fetches. Can be chained.
688- `fetch.doMockIf(urlOrPredicate, bodyOrFunction?, responseInit?):fetch` or `fetch.mockIf` - causes all fetches to be not be mocked unless they match the given string/RegExp/predicate
689 (i.e. "only mock 'fetch' if the request is for the given URL otherwise, use the real fetch implementation")
690- `fetch.dontMockIf(urlOrPredicate, bodyOrFunction?, responseInit?):fetch` - causes all fetches to be mocked unless they match the given string/RegExp/predicate
691 (i.e. "don't mock 'fetch' if the request is for the given URL, otherwise mock the request")
692- `fetch.doMockOnceIf(urlOrPredicate, bodyOrFunction?, responseInit?):fetch` or `fetch.mockOnceIf` - causes the next fetch to be mocked if it matches the given string/RegExp/predicate. Can be chained.
693 (i.e. "only mock 'fetch' if the next request is for the given URL otherwise, use the default behavior")
694- `fetch.dontMockOnceIf(urlOrPredicate):fetch` - causes the next fetch to be not be mocked if it matches the given string/RegExp/predicate. Can be chained.
695 (i.e. "don't mock 'fetch' if the next request is for the given URL, otherwise use the default behavior")
696- `fetch.isMocking(input, init):boolean` - test utility function to see if the given url/request would be mocked.
697 This is not a read only operation and any "MockOnce" will evaluate (and return to the default behavior)
698
699For convenience, all the conditional mocking functions also accept optional parameters after the 1st parameter that call
700 `mockResponse` or `mockResponseOnce` respectively. This allows you to conditionally mock a response in a single call.
701
702#### Conditional Mocking examples
703```js
704
705describe('conditional mocking', () => {
706 const realResponse = 'REAL FETCH RESPONSE'
707 const mockedDefaultResponse = 'MOCKED DEFAULT RESPONSE'
708 const testUrl = defaultRequestUri
709 let crossFetchSpy
710 beforeEach(() => {
711 fetch.resetMocks()
712 fetch.mockResponse(mockedDefaultResponse)
713 crossFetchSpy = jest
714 .spyOn(require('cross-fetch'), 'fetch')
715 .mockImplementation(async () =>
716 Promise.resolve(new Response(realResponse))
717 )
718 })
719
720 afterEach(() => {
721 crossFetchSpy.mockRestore()
722 })
723
724 const expectMocked = async (uri, response = mockedDefaultResponse) => {
725 return expect(request(uri)).resolves.toEqual(response)
726 }
727 const expectUnmocked = async uri => {
728 return expect(request(uri)).resolves.toEqual(realResponse)
729 }
730
731 describe('once', () => {
732 it('default', async () => {
733 const otherResponse = 'other response'
734 fetch.once(otherResponse)
735 await expectMocked(defaultRequestUri, otherResponse)
736 await expectMocked()
737 })
738 it('dont mock once then mock twice', async () => {
739 const otherResponse = 'other response'
740 fetch
741 .dontMockOnce()
742 .once(otherResponse)
743 .once(otherResponse)
744
745 await expectUnmocked()
746 await expectMocked(defaultRequestUri, otherResponse)
747 await expectMocked()
748 })
749 })
750
751 describe('doMockIf', () => {
752 it("doesn't mock normally", async () => {
753 fetch.doMockIf('http://foo')
754 await expectUnmocked()
755 await expectUnmocked()
756 })
757 it('mocks when matches string', async () => {
758 fetch.doMockIf(testUrl)
759 await expectMocked()
760 await expectMocked()
761 })
762 it('mocks when matches regex', async () => {
763 fetch.doMockIf(new RegExp(testUrl))
764 await expectMocked()
765 await expectMocked()
766 })
767 it('mocks when matches predicate', async () => {
768 fetch.doMockIf(input => input === testUrl)
769 await expectMocked()
770 await expectMocked()
771 })
772 })
773
774 describe('dontMockIf', () => {
775 it('mocks normally', async () => {
776 fetch.dontMockIf('http://foo')
777 await expectMocked()
778 await expectMocked()
779 })
780 it('doesnt mock when matches string', async () => {
781 fetch.dontMockIf(testUrl)
782 await expectUnmocked()
783 await expectUnmocked()
784 })
785 it('doesnt mock when matches regex', async () => {
786 fetch.dontMockIf(new RegExp(testUrl))
787 await expectUnmocked()
788 await expectUnmocked()
789 })
790 it('doesnt mock when matches predicate', async () => {
791 fetch.dontMockIf(input => input === testUrl)
792 await expectUnmocked()
793 await expectUnmocked()
794 })
795 })
796
797 describe('doMockOnceIf (default mocked)', () => {
798 it("doesn't mock normally", async () => {
799 fetch.doMockOnceIf('http://foo')
800 await expectUnmocked()
801 await expectMocked()
802 })
803 it('mocks when matches string', async () => {
804 fetch.doMockOnceIf(testUrl)
805 await expectMocked()
806 await expectMocked()
807 })
808 it('mocks when matches regex', async () => {
809 fetch.doMockOnceIf(new RegExp(testUrl))
810 await expectMocked()
811 await expectMocked()
812 })
813 it('mocks when matches predicate', async () => {
814 fetch.doMockOnceIf(input => input === testUrl)
815 await expectMocked()
816 await expectMocked()
817 })
818 })
819
820 describe('dontMockOnceIf (default mocked)', () => {
821 it('mocks normally', async () => {
822 fetch.dontMockOnceIf('http://foo')
823 await expectMocked()
824 await expectMocked()
825 })
826 it('doesnt mock when matches string', async () => {
827 fetch.dontMockOnceIf(testUrl)
828 await expectUnmocked()
829 await expectMocked()
830 })
831 it('doesnt mock when matches regex', async () => {
832 fetch.dontMockOnceIf(new RegExp(testUrl))
833 await expectUnmocked()
834 await expectMocked()
835 })
836 it('doesnt mock when matches predicate', async () => {
837 fetch.dontMockOnceIf(input => input === testUrl)
838 await expectUnmocked()
839 await expectMocked()
840 })
841 })
842
843 describe('doMockOnceIf (default unmocked)', () => {
844 beforeEach(() => {
845 fetch.dontMock()
846 })
847 it("doesn't mock normally", async () => {
848 fetch.doMockOnceIf('http://foo')
849 await expectUnmocked()
850 await expectUnmocked()
851 })
852 it('mocks when matches string', async () => {
853 fetch.doMockOnceIf(testUrl)
854 await expectMocked()
855 await expectUnmocked()
856 })
857 it('mocks when matches regex', async () => {
858 fetch.doMockOnceIf(new RegExp(testUrl))
859 await expectMocked()
860 await expectUnmocked()
861 })
862 it('mocks when matches predicate', async () => {
863 fetch.doMockOnceIf(input => input === testUrl)
864 await expectMocked()
865 await expectUnmocked()
866 })
867 })
868
869 describe('dontMockOnceIf (default unmocked)', () => {
870 beforeEach(() => {
871 fetch.dontMock()
872 })
873 it('mocks normally', async () => {
874 fetch.dontMockOnceIf('http://foo')
875 await expectMocked()
876 await expectUnmocked()
877 })
878 it('doesnt mock when matches string', async () => {
879 fetch.dontMockOnceIf(testUrl)
880 await expectUnmocked()
881 await expectUnmocked()
882 })
883 it('doesnt mock when matches regex', async () => {
884 fetch.dontMockOnceIf(new RegExp(testUrl))
885 await expectUnmocked()
886 await expectUnmocked()
887 })
888 it('doesnt mock when matches predicate', async () => {
889 fetch.dontMockOnceIf(input => input === testUrl)
890 await expectUnmocked()
891 await expectUnmocked()
892 })
893 })
894
895 describe('dont/do mock', () => {
896 test('dontMock', async () => {
897 fetch.dontMock()
898 await expectUnmocked()
899 await expectUnmocked()
900 })
901 test('dontMockOnce', async () => {
902 fetch.dontMockOnce()
903 await expectUnmocked()
904 await expectMocked()
905 })
906 test('doMock', async () => {
907 fetch.dontMock()
908 fetch.doMock()
909 await expectMocked()
910 await expectMocked()
911 })
912 test('doMockOnce', async () => {
913 fetch.dontMock()
914 fetch.doMockOnce()
915 await expectMocked()
916 await expectUnmocked()
917 })
918 })
919
920```
921
922```js
923const expectMocked = async (uri, response = mockedDefaultResponse) => {
924 return expect(request(uri)).resolves.toEqual(response)
925}
926const expectUnmocked = async uri => {
927 return expect(request(uri)).resolves.toEqual(realResponse)
928}
929
930describe('conditional mocking complex', () => {
931 const realResponse = 'REAL FETCH RESPONSE'
932 const mockedDefaultResponse = 'MOCKED DEFAULT RESPONSE'
933 const testUrl = defaultRequestUri
934 let crossFetchSpy
935 beforeEach(() => {
936 fetch.resetMocks()
937 fetch.mockResponse(mockedDefaultResponse)
938 crossFetchSpy = jest
939 .spyOn(require('cross-fetch'), 'fetch')
940 .mockImplementation(async () =>
941 Promise.resolve(new Response(realResponse))
942 )
943 })
944
945 afterEach(() => {
946 crossFetchSpy.mockRestore()
947 })
948
949 describe('complex example', () => {
950 const alternativeUrl = 'http://bar'
951 const alternativeBody = 'ALTERNATIVE RESPONSE'
952 beforeEach(() => {
953 fetch
954 // .mockResponse(mockedDefaultResponse) // set above - here for clarity
955 .mockResponseOnce('1') // 1
956 .mockResponseOnce('2') // 2
957 .mockResponseOnce(async uri =>
958 uri === alternativeUrl ? alternativeBody : '3'
959 ) // 3
960 .mockResponseOnce('4') // 4
961 .mockResponseOnce('5') // 5
962 .mockResponseOnce(async uri =>
963 uri === alternativeUrl ? alternativeBody : mockedDefaultResponse
964 ) // 6
965 })
966
967 describe('default (`doMock`)', () => {
968 beforeEach(() => {
969 fetch
970 // .doMock() // the default - here for clarify
971 .dontMockOnceIf(alternativeUrl)
972 .doMockOnceIf(alternativeUrl)
973 .doMockOnce()
974 .dontMockOnce()
975 })
976
977 test('defaultRequestUri', async () => {
978 await expectMocked(defaultRequestUri, '1') // 1
979 await expectUnmocked(defaultRequestUri) // 2
980 await expectMocked(defaultRequestUri, '3') // 3
981 await expectUnmocked(defaultRequestUri) // 4
982 // after .once('..')
983 await expectMocked(defaultRequestUri, '5') // 5
984 await expectMocked(defaultRequestUri, mockedDefaultResponse) // 6
985 // default 'isMocked' (not 'Once')
986 await expectMocked(defaultRequestUri, mockedDefaultResponse) // 7
987 })
988
989 test('alternativeUrl', async () => {
990 await expectUnmocked(alternativeUrl) // 1
991 await expectMocked(alternativeUrl, '2') // 2
992 await expectMocked(alternativeUrl, alternativeBody) // 3
993 await expectUnmocked(alternativeUrl) // 4
994 // after .once('..')
995 await expectMocked(alternativeUrl, '5') // 5
996 await expectMocked(alternativeUrl, alternativeBody) // 6
997 // default 'isMocked' (not 'Once')
998 await expectMocked(alternativeUrl, mockedDefaultResponse) // 7
999 })
1000 })
1001
1002 describe('dontMock', () => {
1003 beforeEach(() => {
1004 fetch
1005 .dontMock()
1006 .dontMockOnceIf(alternativeUrl)
1007 .doMockOnceIf(alternativeUrl)
1008 .doMockOnce()
1009 .dontMockOnce()
1010 })
1011
1012 test('defaultRequestUri', async () => {
1013 await expectMocked(defaultRequestUri, '1') // 1
1014 await expectUnmocked(defaultRequestUri) // 2
1015 await expectMocked(defaultRequestUri, '3') // 3
1016 await expectUnmocked(defaultRequestUri) // 4
1017 // after .once('..')
1018 await expectUnmocked(defaultRequestUri) // 5
1019 await expectUnmocked(defaultRequestUri) // 6
1020 // default 'isMocked' (not 'Once')
1021 await expectUnmocked(defaultRequestUri) // 7
1022 })
1023
1024 test('alternativeUrl', async () => {
1025 await expectUnmocked(alternativeUrl) // 1
1026 await expectMocked(alternativeUrl, '2') // 2
1027 await expectMocked(alternativeUrl, alternativeBody) // 3
1028 await expectUnmocked(alternativeUrl) // 4
1029 // after .once('..')
1030 await expectUnmocked(alternativeUrl) // 5
1031 await expectUnmocked(alternativeUrl) // 6
1032 // default 'isMocked' (not 'Once')
1033 await expectUnmocked(alternativeUrl) // 7
1034 })
1035 })
1036
1037 describe('doMockIf(alternativeUrl)', () => {
1038 beforeEach(() => {
1039 fetch
1040 .doMockIf(alternativeUrl)
1041 .dontMockOnceIf(alternativeUrl)
1042 .doMockOnceIf(alternativeUrl)
1043 .doMockOnce()
1044 .dontMockOnce()
1045 })
1046
1047 test('defaultRequestUri', async () => {
1048 await expectMocked(defaultRequestUri, '1') // 1
1049 await expectUnmocked(defaultRequestUri) // 2
1050 await expectMocked(defaultRequestUri, '3') // 3
1051 await expectUnmocked(defaultRequestUri) // 4
1052 // after .once('..')
1053 await expectUnmocked(defaultRequestUri) // 5
1054 await expectUnmocked(defaultRequestUri) // 6
1055 // default 'isMocked' (not 'Once')
1056 await expectUnmocked(defaultRequestUri) // 7
1057 })
1058
1059 test('alternativeUrl', async () => {
1060 await expectUnmocked(alternativeUrl) // 1
1061 await expectMocked(alternativeUrl, '2') // 2
1062 await expectMocked(alternativeUrl, alternativeBody) // 3
1063 await expectUnmocked(alternativeUrl) // 4
1064 // after .once('..')
1065 await expectMocked(alternativeUrl, '5') // 5
1066 await expectMocked(alternativeUrl, alternativeBody) // 6
1067 // default 'isMocked' (not 'Once')
1068 await expectMocked(alternativeUrl, mockedDefaultResponse) // 7
1069 })
1070 })
1071
1072 describe('dontMockIf(alternativeUrl)', () => {
1073 beforeEach(() => {
1074 fetch
1075 .dontMockIf(alternativeUrl)
1076 .dontMockOnceIf(alternativeUrl)
1077 .doMockOnceIf(alternativeUrl)
1078 .doMockOnce()
1079 .dontMockOnce()
1080 })
1081
1082 test('defaultRequestUri', async () => {
1083 await expectMocked(defaultRequestUri, '1') // 1
1084 await expectUnmocked(defaultRequestUri) // 2
1085 await expectMocked(defaultRequestUri, '3') // 3
1086 await expectUnmocked(defaultRequestUri) // 4
1087 // after .once('..')
1088 await expectMocked(defaultRequestUri, '5') // 5
1089 await expectMocked(defaultRequestUri, mockedDefaultResponse) // 6
1090 // default 'isMocked' (not 'Once')
1091 await expectMocked(defaultRequestUri, mockedDefaultResponse) // 7
1092 })
1093
1094 test('alternativeUrl', async () => {
1095 await expectUnmocked(alternativeUrl) // 1
1096 await expectMocked(alternativeUrl, '2') // 2
1097 await expectMocked(alternativeUrl, alternativeBody) // 3
1098 await expectUnmocked(alternativeUrl) // 4
1099 // after .once('..')
1100 await expectUnmocked(alternativeUrl) // 5
1101 await expectUnmocked(alternativeUrl) // 6
1102 // default 'isMocked' (not 'Once')
1103 await expectUnmocked(alternativeUrl) // 7
1104 })
1105 })
1106 })
1107})
1108```