API
jest.fn()
Allows us to mock a function
Used when we want to mock a function, and don't really care about its original implementation (often just mocking the return value)
- returns a
spy
(which remembers each call that was made on it) - creates a stub
- useful in removing dependencies to some backend (eg. server, API)
If we pass an argument, then it means we are passing a mock implementation:
const queryMock = jest.fn(() => Promise.resolve({ data: 1 })
console.log(queryMock()) // Promise { pending }
jest.spyOn(object, methodName)
Creates a mock function like jest.fn
, but also tracks calls to the method, allowing us to spy on them
- this allows to ensure that the functions are called as expected (e.g. with correct arguments)
spies still call the original code, they aren’t complete mocks. They are called spies because they "spy" on the implementation code.
jest.spyOn(AutomationsDataSource.prototype, 'deleteAutomation').mockResolvedValue()
Or, we can add conditional logic:
jest.spyOn(AutomationsDataSource.prototype, 'getAutomation').mockImplementation((id) => {
let automation: Automation
switch (id) {
case a1.id:
automation = a1
break
case a2.id:
automation = a2
break
default:
automation = undefined
}
return Promise.resolve(automation)
})
We can also mock as many methods as we want:
jest.spyOn(AutomationsDataSource.prototype, 'getAutomation', 'deleteAutomation').mockImplementation((id) => {
// ...
jest.mock()
jest.mock
allows us to mock an entire module
If we call jest.mock
with only 1 arg,
- the module we pass will be replaced simply with
jest.fn
If we call jest.mock
with 2 args,
- the module we pass will be replaced with the return value of the function provided (arg2).
- in other words, if there is 2 args,
jest.mock
says "replace all occurrences of arg1 with arg2"
- in other words, if there is 2 args,
This code tells jest, "any time calculateAge
is called, stick in 42 as its return value"
- we say "any time you import
calculateAge
into some other module, replace its value withjest.fn(() => 42)
jest.mock('../calculateAge', () => {
return jest.fn(() => 42);
});
// This runs the function specified as second argument to `jest.mock`.
const calculateAge = require('../calculateAge');
calculateAge(); // Will return '42';
Example
jest.mock('../components/box.jsx', () => 'Box')
- this is saying "for the component we are testing, take all instances of the
Box
component and replace them with the textBox
" - what's happening underlying is that since components are just functions, we are saying "replace our component function with the function
() => 'Box'
" - we do this when we don't really care about all the props that come with box, but are just interested in the structure.
<Box />
doesn't really matter, since that isn't what we are testing. - we do this basically when our proptypes fails, because we aren't supplying data in the way proptypes would expect (ex. passing to
Box
margin as25px
rather than the expected25
)
When we do jest.mock('./my-file.js')
, it will turn all functions into Jest mock functions and all objects will have more properties on them like, mockReturnValueOnce
spec: Mocks get hoisted, so if there is a variable you define after the mock, you probably won't be able to use it
jest.mock('./isValidCoupon', () => ({
// isValidCoupon: async () => true,
isValidCoupon: jest.fn().mockImplementation(async () => true),
}))
Mocking individual functions
Imagine our implementation code file is made up of individually exported functions, and we want to mock one of them. We can use jest.requireActual
to import the original implementation, but mock individual functions:
jest.mock('./automationVersions', () => {
const origModule = jest.requireActual('./automationVersions')
return {
...origModule,
doesDraftExist: jest.fn(),
}
})
Then, in our unit test we can mock the return value:
const mockedDoesDraftExist = jest.mocked(doesDraftExist)
mockedDoesDraftExist.mockReturnValue(true)