Skip to content
This repository was archived by the owner on Aug 3, 2023. It is now read-only.

Latest commit

 

History

History
143 lines (101 loc) · 5.98 KB

File metadata and controls

143 lines (101 loc) · 5.98 KB

Jest

Jest is a popular testing library for JavaScript that includes a test runner, assertion library, spies, and code coverage. We strongly recommend Jest for any type of testing that can be run entirely in Node. Jest is the default test framework used by Sewing Kit.

Table of contents

  1. Complementary packages
  2. Best practices
  3. Expectations
  4. Coverage

Complementary packages

We have a number of packages that handle common mocking scenarios that are built specifically for use with Jest:

Best Practices

  • Avoid snapshot tests. As noted in the basic testing principles, we avoid taking too many shortcuts around the assertions a test is meant to perform. Jest’s snapshot feature is often used in place of many assertions (in which case, the test is doing too much). Even when used correctly, snapshot tests move the expected value of the test (and, by extension, the documentation value) into an external file that must be found and understood by the reader. More discussion can be found in our decision log on snapshot tests.

    // bad
    expect(doWork()).toMatchSnapshot();
    
    // good
    expect(doWork()).toHaveProperty('foo', 'bar');
  • Mocks for modules should appear after import statements. Jest will hoist these above the imports, so this simply preserves the typical order of files where imports are at the top.

    // bad
    jest.mock('my-module', () => ({
      foo: 'bar',
    }));
    
    import {foo} from 'my-module';
    
    // good
    import {foo} from 'my-module';
    
    jest.mock('my-module', () => ({
      foo: 'bar',
    }));
  • When mocking out only part of an external module, include the full module as part of your mock.

    Why? Helps prevent unexpected missing members.

    // bad
    import {foo, bar} from 'my-module';
    
    jest.mock('my-module', () => ({
      // Missing bar!
      foo: jest.fn(() => true),
    }));
    
    // good
    import {foo, bar} from 'my-module';
    
    jest.mock('my-module', () => ({
      ...require.requireActual('my-module'),
      foo: jest.fn(() => true),
    }));
  • Do not use jest.doMock(), jest.unmock(), or jest.dontMock().

    Why? jest.doMock() ends up forcing you to require your module directly in the body of tests, which makes the test harder to follow. jest.unmock() and jest.dontMock() are generally indicators that you have too much auto-mocking in place, or that mocking is not scoped sufficiently to only the tests that need it. In general, all tests should either operate on the assumption that a dependency is mocked or that it is not mocked, as this makes it easier to read through all tests in the file.

  • Do not use jest.resetAllMocks(), jest.clearAllMocks(), jest.restoreAllMocks(), and jest.resetModules().

    Why? These methods are overly broad and do not have explicit connections to the contents of your test file. Instead, reset/ clear/ restore individual mocks that are set up explicitly for the purposes of your test suite.

  • When mocking a default export, return your mock on the default key of the module if you are including other parts of the module, and return your mock directly if you are not exporting anything else.

    Why? This helps prevent issues with how Babel/ TypeScript transpile default exports for the version of your file that Node executes.

    // bad
    jest.mock('../my-module', () => ({
      // Probably doesn't do what you think! This will not be the value
      // brought in by a default import (`import myFunction from '../my-module';`)
      // because it is missing a special key that Babel/ TypeScript use to simulate
      // default imports in Node.
      default: jest.fn(),
    }));
    
    // good
    jest.mock('../my-module', () => ({
      ...jest.requireActual('../my-module'),
      default: jest.fn(),
    }));
    
    // or, if there are no other named imports:
    jest.mock('../my-module', () => jest.fn());

Expectations

As noted in our main testing guide, you should prefer the “smartest” assertions available. Below are some common examples of applying this rule to Jest (note that you can always visit the Jest expect guide for a list of available assertions).

// .toHaveProperty()
// prefer:
expect(object).toHaveProperty('foo', 'bar');

// over:
expect(object.foo).toBe('bar');

// .toMatchObject()/ .toEqual()
// prefer:
expect(object).toMatchObject({foo: true, bar: false});
expect(array).toMatchObject([{foo: 'bar'}]);

// or (if you do not want to allow extra properties):

expect(object).toEqual({foo: true, bar: false});
expect(array).toEqual([{foo: 'bar'}]);

// over:
expect(object.foo).toBe(true);
expect(object.bar).toBe(false);
expect(array[0].foo).toBe('bar');

// .toContain()
// prefer:
expect(array).toContain('foo');

// over:
expect(array.includes('foo')).toBe(true);

Coverage

Use Jest’s built-in coverage tool. If you are using Sewing Kit, coverage is automatically collected when running sewing-kit test --coverage.