...
Snapshot testing is inherently brittle, and as such, is good for testing components that will rarely, if ever change. Example of a good candidate for snapshot testing
Mocking
Mocking external libraries
There are several ways to do this with Jest. If only a small part of the library needs to be mocked, the simplest way is to mock it in your test file.
Code Block |
---|
jest.mock('redux-form', () => ({
...jest.requireActual('redux-form'),
// eslint-disable-next-line react/prop-types
Field: ({ label, ...rest }) => <div {...rest}>{label}</div>,
})); |
In the above example, I want all of the functionality of redux-form
, but the Field
components are not under test, and have functionality that I don’t need here, so I’m mocking them and leaving the other functionality alone by using jest.requireActual
.
The other way to mock an external library is by using a manual mock that mocks all parts of the library that you want to mock. This is especially helpful for libraries such as Algolia that call external APIs.
Algolia mock example
Mocking a module
Jest documentation for mocking modules and classes
Often you may want to mock a module in the repo you’re working in, such as an API. There’s some extra syntax for a jest mock that is covering a module:
Code Block |
---|
import LicenseManagerApiService from '../../data/services/LicenseManagerAPIService';
jest.mock('../../data/services/LicenseManagerAPIService', () => ({
__esModule: true,
default: {
fetchSubscriptions: jest.fn(),
},
}));
|
In the above example, the default value of '../../data/services/LicenseManagerAPIService'
is mocked. Under the hood, Jest uses variable hoisting to ensure that the mock of the module actually happens before the import, allowing us to use the mock as we normally would, in this case, mocking a return value from the API:
Code Block |
---|
const subscriptions = Promise.resolve({
data: {
results: {
subscriptions: [sub1, sub2],
},
errors: null,
setErrors: jest.fn(),
forceRefresh: jest.fn(),
loading: false,
},
});
LicenseManagerApiService.fetchSubscriptions.mockReturnValue(subscriptions); |
To mock a non-default part of a module, code like this can be used:
Code Block |
---|
jest.mock('../subscriptions/data/contextHooks', () => ({
__esModule: true,
useSubscriptionFromParams: jest.fn(),
})); |
Testing philosophy
How to avoid brittleness in tests
Rather than using CSS-selectors, prefer first text in the elements (use constants so that tests don’t have to change when text does), then data test ID attributes, so that you can select certain elements in a more reliable way in the course of testing. (This is particularly easy with react-testing-library which has a getByTestId method)
Put microcopy in exported variables and use those in your tests so your tests don’t break every time you change the microcopy
...
Coverage
Start with a threshold that will pass today, and rachet it up over time, say, 10% per quarter
Use tools to fail a build when coverage decreases, such as
coveralls
(used in Paragon). This encourages developers to write tests for all new code.