This project was forked from jest-fetch-mock, and tweaked slightly to run
with Vitest instead of Jest. It is mostly compatible with jest-fetch-mock, with the main difference being that you
need to create fetchMock with a function call, and provide vi to it, rather than relying on a global vi or (jest
in jest-fetch-mock's case). See Usage for more details.
Fetch is the canonical way to do HTTP requests in the browser and other modern runtimes. Vitest 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 Vitest's built-in support for mocking under the surface. This
means that any of the vi.fn() methods are also available. For more information on the Vitest mock API, check their
docs here
As of version 0.4.0, vitest-fetch-mock mocks the global
Fetch method, which is present in all modern runtimes
and browsers. Previous versions used a fetch polyfill to run in older versions of Node.js. See
Compatibility for details.
fetch.mockResponsesfetch.resetMocksfetch.mock to inspect the mock state of each fetch callThe most recent versions of vitest-fetch-mock support Vitest 2 and Node.js 18 and above. If you are using Vitest version
1.x or an older version of node, please install vitest-fetch-mock0.2.2.
To setup your fetch mock you need to do the following things:
$ npm install --save-dev vitest-fetch-mock
Create a setupVitest file to setup the mock or add this to an existing setupFile. :
//setupVitest.js or similar file
import createFetchMock from 'vitest-fetch-mock';
import { vi } from 'vitest';
const fetchMocker = createFetchMock(vi);
// sets globalThis.fetch and globalThis.fetchMock to our mocked version
fetchMocker.enableMocks();
Add the setup file to your vitest config:
"test": {
"setupFiles": [
"./setupVitest.js"
]
}
With 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
If you would like to have the 'fetchMock' available in all tests but not enabled then add fetchMock.dontMock() after
the ...enableMocks() line in setupVitest.js:
import createFetchMock from 'vitest-fetch-mock';
import { vi } from 'vitest';
const fetchMocker = createFetchMock(vi);
// adds the 'fetchMock' global variable and rewires 'fetch' global to call 'fetchMock' instead of the real implementation
fetchMocker.enableMocks();
// changes default behavior of fetchMock to use the real 'fetch' implementation and not mock responses
fetchMocker.dontMock();
If you want a single test file to return to the default behavior of mocking all responses, add the following to the test file:
beforeEach(() => {
// if you have an existing `beforeEach` just add the following line to it
fetchMocker.doMock();
});
To enable mocking for a specific URL only:
beforeEach(() => {
// if you have an existing `beforeEach` just add the following lines to it
fetchMocker.mockIf(/^https?:\/\/example.com.*$/, (req) => {
if (req.url.endsWith('/path1')) {
return 'some response body';
} else if (req.url.endsWith('/path2')) {
return {
body: 'another response body',
headers: {
'X-Some-Response-Header': 'Some header value',
},
};
} else {
return {
status: 404,
body: 'Not Found',
};
}
});
});
If you have changed the default behavior to use the real implementation, you can guarantee the next call to fetch will
be mocked by using the mockOnce function:
fetchMocker.mockOnce('the next call to fetch will always return this as the body');
This function behaves exactly like fetchMocker.once but guarantees the next call to fetch will be mocked even if the
default behavior of fetchMock is to use the real implementation. You can safely convert all you fetchMocker.once calls
to fetchMocker.mockOnce without a risk of changing their behavior.
Add the following line to the start of your test case (before any other imports)
import createFetchMock from 'vitest-fetch-mock';
import { vi } from 'vitest';
const fetchMocker = createFetchMock(vi);
fetchMocker.enableMocks();
If you are using TypeScript and receive errors about the fetchMock global not existing, add a global.d.ts file to
the root of your project (or add the following line to an existing global file):
import 'vitest-fetch-mock';
fetch.mockResponse(bodyOrFunction, init): fetch - Mock all fetch callsfetch.mockResponseOnce(bodyOrFunction, init): fetch - Mock each fetch call independentlyfetch.once(bodyOrFunction, init): fetch - Alias for mockResponseOnce(bodyOrFunction, init)fetch.mockResponses(...responses): fetch - Mock multiple fetch calls independently[bodyOrFunction, init]fetch.mockReject(errorOrFunction): fetch - Mock all fetch calls, letting them fail directlyfetch.mockRejectOnce(errorOrFunction): fetch - Let the next fetch call fail directlyfetch.mockAbort(): fetch - Causes all fetch calls to reject with an "Aborted!" errorfetch.mockAbortOnce(): fetch - Causes the next fetch call to reject with an "Aborted!" errorInstead of passing body, it is also possible to pass a function that returns a promise. The promise should resolve with a string or an object containing body and init props
i.e:
fetch.mockResponse(() => callMyApi().then((res) => ({ body: 'ok' })));
// OR
fetch.mockResponse(() => callMyApi().then((res) => 'ok'));
The function may take an optional "request" parameter of type http.Request:
fetch.mockResponse((req) =>
req.url === 'http://myapi/' ? callMyApi().then((res) => 'ok') : Promise.reject(new Error('bad url'))
);
Note: 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. For example, passing "http://foo.com" to
fetch will result in the request URL being "http://foo.com/" (note the trailing slash).
The same goes for rejects:
fetch.mockReject(() => doMyAsyncJob().then((res) => Promise.reject(res.errorToRaise)));
// OR
fetch.mockReject((req) =>
req.headers.get('content-type') === 'text/plain'
? Promise.reject('invalid content type')
: doMyAsyncJob().then((res) => Promise.reject(res.errorToRaise))
);
fetch.resetMocks() - Clear previously set mocks so they do not bleed into other mocksfetch.enableMocks() - Enable fetch mocking by overriding global.fetch and mocking node-fetchfetch.disableMocks() - Disable fetch mocking and restore default implementation of fetch and/or node-fetchfetch.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
vitest docsFor information on the arguments body and init can take, you can look at the MDN docs on the Response Constructor
function, which vitest-fetch-mock uses under the surface.
https://developer.mozilla.org/en-US/docs/Web/API/Response/Response
Each mocked response or err or will return a MockInstance Function. 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.
In most of the complicated examples below, I am testing my action creators in Redux, but it doesn't have to be used with Redux.
In 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. Make sure the function wrapping your test is marked as async.
Once we've done that we can start to mock our response. We want to give it an object with 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.
We then call the function that we want to test with the arguments we want to test with. We use await to wait until the
response resolves, and then assert we have got the correct data back.
Finally we can assert on the .mock state that Vitest provides for us to test what arguments were given to fetch and
how many times it was called
//api.js
export function APIRequest(who) {
if (who === 'google') {
return fetch('https://google.com').then((res) => res.json());
} else {
return 'no argument provided';
}
}
//api.test.js
import { APIRequest } from './api';
describe('testing api', () => {
beforeEach(() => {
fetch.resetMocks();
});
it('calls google and returns data to me', async () => {
fetch.mockResponseOnce(JSON.stringify({ data: '12345' }));
//assert on the response
const res = await APIRequest('google');
expect(res.data).toEqual('12345');
//assert on the times called and arguments given to fetch
expect(fetch.requests().length).toEqual(1);
expect(fetch.requests()[0].url).toEqual('https://google.com/');
});
});
In 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.
import configureMockStore from 'redux-mock-store'; // mock store
import thunk from 'redux-thunk';
const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);
import { getAccessToken } from './accessToken';
describe('Access token action creators', () => {
it('dispatches the correct actions on successful fetch request', () => {
fetch.mockResponse(JSON.stringify({ access_token: '12345' }));
const expectedActions = [{ type: 'SET_ACCESS_TOKEN', token: { access_token: '12345' } }];
const store = mockStore({ config: { token: '' } });
return (
store
.dispatch(getAccessToken())
//getAccessToken contains the fetch call
.then(() => {
// return of async actions
expect(store.getActions()).toEqual(expectedActions);
})
);
});
});
In 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.
import configureMockStore from 'redux-mock-store'; // mock store
import thunk from 'redux-thunk';
const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);
import { getAccessToken } from './accessToken';
describe('Access token action creators', () => {
it('dispatches the correct actions on a failed fetch request', () => {
fetch.mockReject(new Error('fake error message'));
const expectedActions = [{ type: 'SET_ACCESS_TOKEN_FAILED', error: { status: 503 } }];
const store = mockStore({ config: { token: '' } });
return (
store
.dispatch(getAccessToken())
//getAccessToken contains the fetch call
.then(() => {
// return of async actions
expect(store.getActions()).toEqual(expectedActions);
})
);
});
});
Fetches can be mocked to act as if they were aborted during the request. This can be done in 4 ways:
```js describe('Mocking aborts', () => { beforeEach(() => { fetch.resetMocks(); fetc
$ claude mcp add vitest-fetch-mock \
-- python -m otcore.mcp_server <graph>