-
Notifications
You must be signed in to change notification settings - Fork 177
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: start implementing jest and vitest wrappers
- Loading branch information
1 parent
ef0fd52
commit 804f885
Showing
19 changed files
with
2,526 additions
and
0 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
# fetch-mock-jest | ||
|
||
Wrapper around [fetch-mock](http://www.wheresrhys.co.uk/fetch-mock) - a comprehensive, isomorphic mock for the [fetch api](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) - which provides an interface that is more idiomatic when working in [jest](https://jestjs.io). | ||
|
||
The example at the bottom of this readme demonstrates the intuitive API, but shows off only a fraction of fetch-mock's functionality. Features include: | ||
|
||
- mocks most of the fetch API spec, even advanced behaviours such as streaming and aborting | ||
- declarative matching for most aspects of a http request, including url, headers, body and query parameters | ||
- shorthands for the most commonly used features, such as matching a http method or matching one fetch only | ||
- support for delaying responses, or using your own async functions to define custom race conditions | ||
- can be used as a spy to observe real network requests | ||
- isomorphic, and supports either a global fetch instance or a locally required instanceg | ||
|
||
# Requirements | ||
|
||
fetch-mock-jest requires the following to run: | ||
|
||
- [Node.js](https://Node.js.org/) 8+ for full feature operation | ||
- [Node.js](https://Node.js.org/) 0.12+ with [limitations](http://www.wheresrhys.co.uk/fetch-mock/installation) | ||
- [npm](https://www.npmjs.com/package/npm) (normally comes with Node.js) | ||
- [jest](https://www.npmjs.com/package/jest) 25+ (may work with earlier versions, but untested) | ||
- Either | ||
- [node-fetch](https://www.npmjs.com/package/node-fetch) when testing in Node.js. To allow users a choice over which version to use, `node-fetch` is not included as a dependency of `fetch-mock`. | ||
- A browser that supports the `fetch` API either natively or via a [polyfill/ponyfill](https://ponyfoo.com/articles/polyfills-or-ponyfills) | ||
|
||
# Installation | ||
|
||
`npm install -D fetch-mock-jest` | ||
|
||
## global fetch | ||
|
||
`const fetchMock = require('fetch-mock-jest')` | ||
|
||
## node-fetch | ||
|
||
``` | ||
jest.mock('node-fetch', () => require('fetch-mock-jest').sandbox()) | ||
const fetchMock = require('node-fetch') | ||
``` | ||
|
||
# API | ||
|
||
## Setting up mocks | ||
|
||
Please refer to the [fetch-mock documentation](http://wheresrhys.co.uk/fetch-mock) and [cheatsheet](https://github.com/wheresrhys/fetch-mock/blob/master/docs/cheatsheet.md) | ||
|
||
All jest methods for configuring mock functions are disabled as fetch-mock's own methods should always be used | ||
|
||
## Inspecting mocks | ||
|
||
All the built in jest function inspection assertions can be used, e.g. `expect(fetchMock).toHaveBeenCalledWith('http://example.com')`. | ||
|
||
`fetchMock.mock.calls` and `fetchMock.mock.results` are also exposed, giving access to manually inspect the calls. | ||
|
||
The following custom jest expectation methods, proxying through to `fetch-mock`'s inspection methods are also available. They can all be prefixed with the `.not` helper for negative assertions. | ||
|
||
- `expect(fetchMock).toHaveFetched(filter, options)` | ||
- `expect(fetchMock).toHaveLastFetched(filter, options)` | ||
- `expect(fetchMock).toHaveNthFetched(n, filter, options)` | ||
- `expect(fetchMock).toHaveFetchedTimes(n, filter, options)` | ||
- `expect(fetchMock).toBeDone(filter)` | ||
|
||
### Notes | ||
|
||
- `filter` and `options` are the same as those used by [`fetch-mock`'s inspection methods](http://www.wheresrhys.co.uk/fetch-mock/#api-inspectionfundamentals) | ||
- The obove methods can have `Fetched` replaced by any of the following verbs to scope to a particular method: + Got + Posted + Put + Deleted + FetchedHead + Patched | ||
|
||
e.g. `expect(fetchMock).toHaveLastPatched(filter, options)` | ||
|
||
## Tearing down mocks | ||
|
||
`fetchMock.mockClear()` can be used to reset the call history | ||
|
||
`fetchMock.mockReset()` can be used to remove all configured mocks | ||
|
||
Please report any bugs in resetting mocks on the [issues board](https://github.com/wheresrhys/fetch-mock-jest/issues) | ||
|
||
# Example | ||
|
||
```js | ||
const fetchMock = require('fetch-mock-jest'); | ||
const userManager = require('../src/user-manager'); | ||
|
||
test(async () => { | ||
const users = [{ name: 'bob' }]; | ||
fetchMock | ||
.get('http://example.com/users', users) | ||
.post('http://example.com/user', (url, options) => { | ||
if (typeof options.body.name === 'string') { | ||
users.push(options.body); | ||
return 202; | ||
} | ||
return 400; | ||
}) | ||
.patch( | ||
{ | ||
url: 'http://example.com/user' | ||
}, | ||
405 | ||
); | ||
|
||
expect(await userManager.getAll()).toEqual([{ name: 'bob' }]); | ||
expect(fetchMock).toHaveLastFetched('http://example.com/users | ||
get'); | ||
await userManager.create({ name: true }); | ||
expect(fetchMock).toHaveLastFetched( | ||
{ | ||
url: 'http://example.com/user', | ||
body: { name: true } | ||
}, | ||
'post' | ||
); | ||
expect(await userManager.getAll()).toEqual([{ name: 'bob' }]); | ||
fetchMock.mockClear(); | ||
await userManager.create({ name: 'sarah' }); | ||
expect(fetchMock).toHaveLastFetched( | ||
{ | ||
url: 'http://example.com/user', | ||
body: { name: 'sarah' } | ||
}, | ||
'post' | ||
); | ||
expect(await userManager.getAll()).toEqual([ | ||
{ name: 'bob' }, | ||
{ name: 'sarah' } | ||
]); | ||
fetchMock.mockReset(); | ||
}); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
/*global jest, beforeAll, afterAll */ | ||
jest.mock('node-fetch', () => require('../server').sandbox()); | ||
const fetch = require('node-fetch'); | ||
describe('jest built-ins', () => { | ||
describe('exposing mock internals', () => { | ||
beforeAll(() => { | ||
fetch.mock('http://example.com', 200).mock('http://example2.com', 201); | ||
fetch('http://example.com', { | ||
headers: { | ||
test: 'header', | ||
}, | ||
}); | ||
fetch('http://example2.com', { | ||
headers: { | ||
test: 'header2', | ||
}, | ||
}); | ||
}); | ||
|
||
afterAll(() => fetch.reset()); | ||
it('exposes `calls` property', () => { | ||
expect(fetch.mock.calls).toBeDefined(); | ||
expect(fetch.mock.calls.length).toBe(2); | ||
expect(fetch.mock.calls).toMatchObject([ | ||
[ | ||
'http://example.com', | ||
{ | ||
headers: { | ||
test: 'header', | ||
}, | ||
}, | ||
], | ||
[ | ||
'http://example2.com', | ||
{ | ||
headers: { | ||
test: 'header2', | ||
}, | ||
}, | ||
], | ||
]); | ||
}); | ||
it('exposes `results` property', async () => { | ||
expect(fetch.mock.results).toBeDefined(); | ||
expect(fetch.mock.results.length).toEqual(2); | ||
expect(await fetch.mock.results[0].value).toMatchObject({ | ||
status: 200, | ||
}); | ||
expect(await fetch.mock.results[1].value).toMatchObject({ | ||
status: 201, | ||
}); | ||
}); | ||
}); | ||
|
||
describe('clearing', () => { | ||
beforeEach(() => { | ||
fetch.mock('http://example.com', 200).mock('http://example2.com', 201); | ||
fetch('http://example.com', { | ||
headers: { | ||
test: 'header', | ||
}, | ||
}); | ||
fetch('http://example2.com', { | ||
headers: { | ||
test: 'header2', | ||
}, | ||
}); | ||
}); | ||
|
||
afterEach(() => fetch.reset()); | ||
it('mockClear', () => { | ||
expect(fetch.mockClear).toBeDefined(); | ||
fetch.mockClear(); | ||
expect(fetch.mock.calls.length).toEqual(0); | ||
expect(fetch._calls.length).toEqual(0); | ||
expect(fetch.routes.length).toEqual(2); | ||
}); | ||
it('mockReset', () => { | ||
expect(fetch.mockReset).toBeDefined(); | ||
fetch.mockReset(); | ||
expect(fetch.mock.calls.length).toEqual(0); | ||
expect(fetch._calls.length).toEqual(0); | ||
expect(fetch.routes.length).toEqual(0); | ||
}); | ||
it('mockRestore', () => { | ||
expect(() => fetch.mockRestore()).toThrow( | ||
"mockRestore not supported on fetch-mock. Use fetch-mock's methods to manage mock responses" | ||
); | ||
}); | ||
it('mockImplementation', () => { | ||
expect(() => fetch.mockImplementation()).toThrow( | ||
"mockImplementation not supported on fetch-mock. Use fetch-mock's methods to manage mock responses" | ||
); | ||
}); | ||
it('mockImplementationOnce', () => { | ||
expect(() => fetch.mockImplementationOnce()).toThrow( | ||
"mockImplementationOnce not supported on fetch-mock. Use fetch-mock's methods to manage mock responses" | ||
); | ||
}); | ||
it('mockName', () => { | ||
expect(() => fetch.mockName()).toThrow( | ||
"mockName not supported on fetch-mock. Use fetch-mock's methods to manage mock responses" | ||
); | ||
}); | ||
it('mockReturnThis', () => { | ||
expect(() => fetch.mockReturnThis()).toThrow( | ||
"mockReturnThis not supported on fetch-mock. Use fetch-mock's methods to manage mock responses" | ||
); | ||
}); | ||
it('mockReturnValue', () => { | ||
expect(() => fetch.mockReturnValue()).toThrow( | ||
"mockReturnValue not supported on fetch-mock. Use fetch-mock's methods to manage mock responses" | ||
); | ||
}); | ||
it('mockReturnValueOnce', () => { | ||
expect(() => fetch.mockReturnValueOnce()).toThrow( | ||
"mockReturnValueOnce not supported on fetch-mock. Use fetch-mock's methods to manage mock responses" | ||
); | ||
}); | ||
it('mockResolvedValue', () => { | ||
expect(() => fetch.mockResolvedValue()).toThrow( | ||
"mockResolvedValue not supported on fetch-mock. Use fetch-mock's methods to manage mock responses" | ||
); | ||
}); | ||
it('mockResolvedValueOnce', () => { | ||
expect(() => fetch.mockResolvedValueOnce()).toThrow( | ||
"mockResolvedValueOnce not supported on fetch-mock. Use fetch-mock's methods to manage mock responses" | ||
); | ||
}); | ||
it('mockRejectedValue', () => { | ||
expect(() => fetch.mockRejectedValue()).toThrow( | ||
"mockRejectedValue not supported on fetch-mock. Use fetch-mock's methods to manage mock responses" | ||
); | ||
}); | ||
it('mockRejectedValueOnce', () => { | ||
expect(() => fetch.mockRejectedValueOnce()).toThrow( | ||
"mockRejectedValueOnce not supported on fetch-mock. Use fetch-mock's methods to manage mock responses" | ||
); | ||
}); | ||
}); | ||
describe('native jest mock function inspectors', () => { | ||
it('.toHaveBeenCalled()', () => { | ||
expect(() => expect(fetch).toHaveBeenCalled()).not.toThrow(); | ||
}); | ||
// Just want to get the fix out for calling fetch methods | ||
// These will all work as the basic mechanism is fixed, but | ||
// no time to set up all th etest cases now | ||
it.skip('.toHaveBeenCalledTimes(number)', () => { | ||
expect(() => expect(fetch).toHaveBeenCalledTimes(1)).not.toThrow(); | ||
}); | ||
it.skip('.toHaveBeenCalledWith(arg1, arg2, ...)', () => { | ||
expect(() => expect(fetch).toHaveBeenCalledWith(1)).not.toThrow(); | ||
}); | ||
it.skip('.toHaveBeenLastCalledWith(arg1, arg2, ...)', () => { | ||
expect(() => expect(fetch).toHaveBeenLastCalledWith(1)).not.toThrow(); | ||
}); | ||
it.skip('.toHaveBeenNthCalledWith(nthCall, arg1, arg2, ....)', () => { | ||
expect(() => expect(fetch).toHaveBeenNthCalledWith(1, 1)).not.toThrow(); | ||
}); | ||
it.skip('.toHaveReturned()', () => { | ||
expect(() => expect(fetch).toHaveReturned()).not.toThrow(); | ||
}); | ||
it.skip('.toHaveReturnedTimes(number)', () => { | ||
expect(() => expect(fetch).toHaveReturnedTimes(1)).not.toThrow(); | ||
}); | ||
it.skip('.toHaveReturnedWith(value)', () => { | ||
expect(() => expect(fetch).toHaveReturnedWith(1)).not.toThrow(); | ||
}); | ||
it.skip('.toHaveLastReturnedWith(value)', () => { | ||
expect(() => expect(fetch).toHaveLastReturnedWith(1)).not.toThrow(); | ||
}); | ||
it.skip('.toHaveNthReturnedWith(nthCall, value)', () => { | ||
expect(() => expect(fetch).toHaveNthReturnedWith(1, 1)).not.toThrow(); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.