Testing React with Jest and Enzyme II

Dominic Fraser
codeburst
Published in
4 min readNov 12, 2018

--

Previously we looked at how to setup Jest and Enzyme with some and took a slight detour to look at mocking ES and CommonJS modules with Jest.

In this post we are going to look at some additional examples of how to simulate user interaction with a component via Enzyme to create specific test scenarios.

We will be using Enzyme’s mount for full DOM rendering as described in the first post of this series. To summarise, from the docs:

Full DOM rendering is ideal for use cases where you have components that may interact with DOM APIs or need to test components that are wrapped in higher order components.

If you do not want to run your tests inside of a browser, the recommended approach to using mount is to depend on a library called jsdom which is essentially a headless browser implemented completely in JS.

This is what gives us the ability to interact with our React component as if we were inside a browser.

The examples given are based on the following versions:

"enzyme": "^3.3.0",
"enzyme-adapter-react-16": "^1.1.1",
"enzyme-to-json": "^3.3.3",

Simulating events

Enzyme syntax for simulating user interaction is straight forward to read, at it simplest as below on a mounted component:

component.find(selector).simulate(event);

Selectors

Selectors can be one of a:

  • CSS Selector
  • Prop Attribute Selector
  • Prop Object Selector
  • React Component Constructor
  • React Component displayName

In the examples covered here we will just look at the syntax behind different ways of targeting CSS selectors. The Enzyme docs are well written and contain good examples to continue reading if a CSS selector does not fit your purpose.

Directly from the documentation we can see that the following are supported:

  • Class syntax (.foo, .foo-bar, etc.)
  • Element syntax (input, div, span, etc.)
  • ID syntax (#foo, #foo-bar, etc.)
  • Attribute syntax ([href="foo"], [type="text"], etc.)

With combinations of the above possible (button#id-foo, etc).

A common error to see is Method "simulate" is only meant to be run on a single node. 3 found instead. if a sub-component being interacted with is used more than once in the parent.

This is a simple fix, as it is possible to specify the index of the node you wish to interact with:

// the initialcomponent.find([className="checkbox__input"]).simulate(event);// becomescomponent.find([className="checkbox__input"]).at(1).simulate(event);

Events

Enzyme’s simulate is used for simulating DOM Events, common ones being ‘click’, ‘change’, and ‘keydown’. After the node is selected simulate is chained on as to complete the mock interaction:

component.find(selector).simulate('click');component.find(selector).simulate('change');component.find(selector).simulate('keydown', { keyCode: 32 });

Writing tests

Testing re-rendering

If a DOM event causes a re-render, unmounting of a child component for example, then Jest Snapshot testing can be used to test the component renders as expected.

it('should be possible to open menu with Spacebar', done => {
const component = mount(<MyComponent />);
component.find('#link-id').simulate('keydown', { keyCode: 32 }); expect(component).toMatchSnapshot(); component.unmount();
});

Testing function calls

If a function is passed to a child component you may wish to test that it is called correctly when mounting the parent. The function is first mocked and passed as the correct prop.

const mockFunction = jest.fn();it('should call mockFunction on button click', () => {
const component = mount(
<MyComponent onClickFunction={mockFunction} />
);
component.find('button#ok-btn').simulate('click'); expect(mockFunction).toHaveBeenCalled();

component.unmount();
});

Testing state or prop updates

The component state and props are also possible to evaluate.

it('sets loading state to true on save press', () => {
const component = mount(<MyComponent />);
component.find('[className="save-button"]').simulate('click'); expect(component.state('isLoading')).toEqual(true); component.unmount();
});

Other interactions with the document

As mount has access to a full DOM many other aspects can be included in tests.

This includes the cookies associated with the current document. These are accessible via document.cookie. To prevent changes from persisting between tests you may wish to use something such as

beforeEach(() => {
document.cookie = '';
});

to reset their values.

If the component synced a cookie value to what is in state on mount then the following test could be considered:

it('syncs state with user cookie on mount', () => {
document.cookie = 'cookieOne=valueOne:::false&valueTwo:::true';

const component = mount(<MyComponent />);
expect(component.state('valueOne')).toEqual(false);
expect(component.state('valueTwo')).toEqual(true);
component.unmount();
});

Final thoughts

Hopefully this provides an indication of the possibilities that exist when using Jest and Enzyme together, and the examples here are readable enough to use the parts that apply to your own projects.

An additional tip is that if user interactions are not producing the results that you are expected debugging can be aided with adding the Enzyme specific

console.log(component.debug())

to see a snapshot of the mounted component at that time.

Thanks for reading, and if you have posts of your own that expand on this feel free to link them in the comments! 😁

Other posts I have written include:

--

--