How to Mock Using Jest.spyOn (Part 2)
This is part 2 of my JavaScript testing series. You can check out How to Write Functional Tests in React (Part 1) here!
Mocking is an important concept in testing where a subject that is dependent on data has said data replaced so that it can be controlled and measured.
There are a handful of ways you can mock in Jest. You can mock a function with jest.fn
or mock a module with jest.mock
, but my preferred method of mocking is by using jest.spyOn
.
Tracking Calls
jest.spyOn
allows you to mock either the whole module or the individual functions of the module. At its most general usage, it can be used to track calls on a method:
const video = { play() { return true; }, }; export default video;
import video from './video'; test('plays video', () => { const spy = jest.spyOn(video, 'play'); video.play(); expect(spy).toHaveBeenCalledTimes(1); }); view raw
Any call to video.play
, either in this test file or (if the function is being called as a side-effect) in some other file, will be tracked within this test('plays video', () =>{}
) function.
Overwriting/Mocking A Function
If you want to mock out an implementation and overwrite the original function, you can chain .mockImplementation
to the end of jest.spyOn
:
test('creates Contract on correct date', () => { const NOW = '2019-05-03T08:00:00.000Z'; const mockDateNow = jest .spyOn(global.Date, 'now') .mockImplementation(() => new Date(NOW).getTime()); const mutation = ` mutation createContract { createContract { startedOn } } `; const response = await graphQLRequestAsUser(mutation); const { data } = response.body; expect(data.startedOn).toEqual(NOW); mockDateNow.mockRestore(); });
In this test, we are mocking the implementation of JavaScript’s global Date.now()
function. We set it to return a static value of 2019-05-03T08:00:00.000Z
(converted to milliseconds).
On the backend, where createContract
is implemented, we use Date.now()
to define the startedOn
field of a Contract
. When graphQLRequestAsUser
is called, createContract
will be run on the backend and will use the mocked Date.now()
to return the date we set (instead of the actual Date.now()
).
By mocking Date.now()
, we can ensure that:
- createContract is creating Contract records with a startedOn field in the correct format
- our tests will pass consistently because Date.now() is returning a value that we can confirm every time
BONUS: before/after Hooks
We can use jest’s beforeEach
and afterEach
hooks so that we keep our tests DRY when they have similar environments to setup:
const now = '2019-05-03T08:00:00.000Z'; let mockDateNow; beforeEach(() => { mockDateNow = jest.spyOn(global.Date, 'now').mockImplementation(() => new Date(now).getTime()); }); afterEach(() => { mockDateNow.mockRestore(); }); test('some test name', () => { //...do some test stuff here }) test('another test name', () => { //...do some more test stuff here })
Before every test
function is run in the file, jest will mock Date.now()
, and after every test
, jest will restore the function to its original implementation. Using the beforeEach/afterEach
hooks for setup ensures that every test is fresh and independent of each other.
Note: we could probably use beforeAll/afterAll
instead of the tests aren’t mutating the date.
I hope this article helped you understand the usage of mocks a litter better. Please don’t hesitate to reach out if you have any questions. You can drop us a line through Echobind’s contact page or just leave a comment here. I wish you the best of luck to you on your next npm run jest!