Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

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.