Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Table of Contents

This page serves as a placeholder to document examples and patterns of “good” code that we feel is worthwhile to showcase to others to follow as a “best practice”, convention, or pattern. This may include things in various categories of frontend development such as:

...

These examples may be in the form of code snippets pasted here, or links to examples in our code repositories, etc.

Testing Libraries

We use two main testing libraries, enzyme and react-testing-library. We have a preference for react-testing-library for new code.

Enzyme

You should use enzyme if needed; leave a comment as to why you’re using it.

What it’s good at

  • letting you change the component state directly (without a simulated click). However, this is not a pattern that we want to encourage in testing, because users can’t directly manipulate state or props, so we don’t want our tests to.

  • letting you introspect what is in the component’s state / props

  • pure unit testing. If you’re using a library like Paragon, and just want to test that you’re passing the correct props to the component, enzyme might be a good choice

  • Testing react class components - allows you to unit test class methods by calling them directly

Disadvantages

  • It can encourage bad testing patterns by allowing the developer to directly manipulate state

  • It can encourage bad testing patterns by allowing users to introspect state / props rather than test what the user is seeing in the DOM

Examples

react-testing-library

This is our preferred testing library

What it’s good at

  • Integration testing

  • Easy to access components via what the user would see in the dom

  • Testing React functional components

Disadvantages

  • More difficult to check if you’re passing props correctly / introspect on component’s inner working

  • Difficult to unit test class components

Snapshot testing

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

...

Child pages (Children Display)