Testing with Enzyme

Up to now we have been successfully running tests against the frontend using Jest but in the previous chapter we added a new module react-onclickoutside which enables the user to close the mobile fly out menu by clicking anywhere outside the menu. Here’s the gotcha! The react-onclickoutside module is known as a HOC, (Higher-Order Component), and the Jest testing framework we’ve been using cannot test HOCs so, if we were to run our frontend tests — one of our test would fail.

Despair not!

We can use an additional tool called Enzyme which can successfully test HOCs.

npm i --save-dev enzyme enzyme-adapter-react-17-updated enzyme-to-json jest raf react-addons-shallow-compare react-test-renderer

What are Higher-Order Components?

Drawing from React’s official docs for Higher-Order Components; a higher-order component is a function that takes a component and returns a new component.

Wha… ?

In the course of this tutorial we have been writing components that transforms props into a UI, (user interface). But since the react-onclickoutside module is a HOC, the react-onclickoutside module is taking our Navbar component, (frontend/src/components/navbar.js), and transforming it into a new Navbar.handleClickOutside component which listens for mouse clicks outside of the navbar menu.

If we were to run our frontend tests now they would fail because we are trying to test the navbar component but the navbar component is now a child component of react-onclickoutside component.

To be honest I have not dug into the inner workings of react-onclickoutside other than to know that this component is watching for a state change e.g. mouse click outside of the wrapped component.

So, where does that leave us? One possible solution is to run only a shallow test which targets to the parent Header component but Jest doesn’t provide shallow testing hence the need of an additionial testing utility of Enzyme which does provide shallow testing.

So, Enzyme it is — let the games commence!

Configuring Enzyme

setupTests.js

Create a file for setupTests.js in the root of the frontend directory and populate the files with the following:

import Enzyme from "enzyme";
import Adapter from "enzyme-adapter-react-17-updated";

Enzyme.configure({
  adapter: new Adapter()
});

// a global graphql is expected by gatsby
global.graphql = () => ''

jest.config.json

Updating the jest config file add paths to the raf/polyfill and the setupTests.js file we just created. As well setup the snapshotSerializers.

module.exports = {
  transform: {
    "^.+\\.jsx?$": `<rootDir>/jest-preprocess.js`,
  },
  moduleNameMapper: {
    ".+\\.(css|styl|less|sass|scss)$": `identity-obj-proxy`,
    ".+\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": `<rootDir>/__mocks__/file-mock.js`,
  },
  testPathIgnorePatterns: [`node_modules`, `\\.cache`, `<rootDir>.*/public`],
  transformIgnorePatterns: [`node_modules/(?!(gatsby)/)`],
  globals: {
    __PATH_PREFIX__: ``,
  },
  testURL: `http://localhost`,
  setupFiles: [`<rootDir>/loadershim.js`, `raf/polyfill`, `<rootDir>/setupTests.js`],
  snapshotSerializers: ["enzyme-to-json/serializer"],
}

Refactor Test for Header

We are going to refactor our Header test as follows:

// frontend/src/components/tests/header.js

import React from 'react';
import { PureHeader as Header } from '../header';
import renderer from 'react-test-renderer';
import { shallow } from 'enzyme'

describe("Header", () => {
  const data = {
    site: {
      siteMetadata: {
        title: "Gatsby Todo App"
      },
    },
  }

  it("renders correctly", () => {
    const wrapper = shallow(<Header data={data} />)
    expect(wrapper).toMatchSnapshot()
  })
})

We need to rebuild the frontend since we’ve updated and added new config files:

docker-compose build --no-cache frontend

Before running tests on the frontend might be prudent to update our frontend snapshot:

make update-snapshot
# or
docker-compose run frontend npm test -- --u

# run frontend tests
make test
# or 
docker-compose run frontend npm test

If all goes well, our frontend tests should pass:

> jest

 PASS  src/components/__tests__/header.js
  Header
    ✓ renders correctly (17 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   1 passed, 1 total
Time:        4.571 s
Ran all test suites.

Voila! Our tests passed.

What’s next?