Edit ContentUse this to fix an issue on this page
View PresentationOpen presentation associated with this section

Insights

Below are some examples / patterns / recipes / approaches you may find useful when developing.

Watch Mode

Following Test Driven Development you will write your tests, and develop your code until your tests are green.

The watch mechanism incorporated in to Jest is very powerful in this scenario. It can be activated with yarn run test --watch

Debugging

It is possible to debug your Jest tests with ease. First add a debugger; statement as appropriate:

import * as metricsService from "./metrics-service";

export default function ({ eventType }) {
  debugger;
  metricsService.send({
    event: `my_application_namespace.${eventType}`,
    time: Date.now(),
  });
}

The command to run your tests is slightly different:

# Run all tests. `--runInBand` stops Jest from running in parallel.
$ node --inspect-brk node_modules/.bin/jest --runInBand

# Running particular file
$ node --inspect-brk node_modules/.bin/jest --runInBand ./src/metrics

We can now see that the Jest process is paused until a debugger attaches:

paused jest

There are various debuggers available, however for this workshop we will use Chrome's. It is accessible via:

chrome://inspect/#devices

As a full example:

Current Time 0:00
/
Duration Time 0:00
Progress: NaN%

At the time of writing there is also support for:

Test structure

Although test descriptions and structure can be arbitrary, it can be useful to follow similar conventions. Although aimed at Ruby, you can follow some of the approaches recommended by http://www.betterspecs.org/

For instance, you may like the convention for writing descriptions such as:

  • # to describe functions you wish to test, #add
  • :: to describe class methods

You may also find it easier to have one single expectation per test. This means if you are applying TDD, you can gradually make your tests pass, rather than attempt to make one test pass - which actually contains multiple assertions.

This can be combined with using it.only to focus on one particular assertion at a time.

For example:

import * as service from "../";

describe("movies-api", function () {
  describe("#fetchMovies", function () {
    describe("when there are no movies available", function () {
      let testContext;
      beforeEach(async function () {
        testContext = {};

        const fetchMock = jest.spyOn(global, "fetch");
        fetchMock.mockReturnValueOnce(
          Promise.resolve({
            json: () => Promise.resolve({ movies: [] }),
          })
        );

        testContext.result = await service.fetchMovies();      });

      it("returns the data", function () {        expect(testContext.result).toEqual({ movies: [] });      });
      it("fetches from the correct endpoint", function () {
        expect(fetch).toHaveBeenCalledWith(
          "http://www.alanfoster.me/movies.json"
        );
      });
    });
  });
});

Adding matchers

You can extend Jest's default matchers. There are various open source libraries available, for instance:

Or you can add your own custom matchers:

expect.extend({
  toBePotato(received) {
    if (received === "potato") {
      return {
        message: () => `expected ${received} to not be potato`,
        pass: true,
      };
    } else {
      return {
        message: () => `expected ${received} to be potato`,
        pass: false,
      };
    }
  },
});

it("works successfully", function () {
  expect("potato").toBePotato();
  expect("sausage roll").not.toBePotato();
});

There is also support for colored diffing etc, full details.

Difficulty testing?

If your code is hard to test, perhaps there is an issue with your code. A common testing smell is a test which heavily relies on various complex mocks, or one which requires many imports.