Enzyme Testing Quick Reference

Gina Benavidez
5 min readJun 18, 2018

--

This is a quick guide to testing react components with Enzyme + Chai + Mocha. I’ll cover a few basics like rendering your components and traversing the HTML, and later I’ll discuss some Mocha/ Chai functionality. This article is meant to serve as a quick reference with lots of practical examples rather than a full explanation, I’ll be dropping links at the bottom and answering comments if you would like a better explanation.

Enzyme: Creating a wrapper with Shallow, Mount and Render

Before you can begin to make assertions and test a component, you have to simulate a rendition of it.This means creating whats commonly known as a “wrapper”, you can establish a wrapper in one of the following three ways:

  • Rendering with shallow: helpful for testing your component as a unit, shallow ensures that you aren’t making assertions on child components.
  • Rendering with mount: ideal for testing component life cycles and DOM API.
  • Render with render: renders static HTML.
describe('Foo Component', () => {
it('will render with shallow', () => {
const wrapper = shallow(<Foo />);
});
it('will render with mount', () => {
const wrapper = mount(<Foo />);
});
it('will render with render', () => {
const wrapper = render(<Foo />);
});
});

As far as the API goes, shallow and mount are similar, but I’ll point out differences as they come up. Testing withrender is best for testing static HTML renditions, and personally I don’t find it to many use cases for it.

Basic Enzyme Methods

Here are some of the enzyme methods that will help you manipulate and traverse your rendered components.

Find

This method is really useful when you need to single out labels, forms or when you need to target a specific div. This is a go to method for traversing the DOM.

describe("Foo Component", () => { 
it("will have one label", () => {
const wrapper = shallow(<Foo />);
expect(wrapper.find("label")).to.have.length(1);
});
});

Instance

When you render your component Enzyme gives you a toolkit for manipulating that simulated instance of your component. So if “Foo Component” had a method called handleFocus()we might want to fire that method for testing purposes. You would do that like so:

describe('Foo', () => {
it("has a method called handleFocus", () => {
const wrapper = shallow(<Foo />);
wrapper.instance().handleFocus(); // calls handleFocus
});
});

SetProps & SetState

If you need to, you can also set props, and manipulate the state of your component within a test. This is also helpful for testing component life cycles.

describe("Foo", () => {
it("can take props & state through Enzyme tools!", () => {
const wrapper = shallow(<Foo />);
wrapper.setProps({ foo: "gbenavid" }); // setProps re-renders <Foo />
wrapper.setState({ bar: "K1TT3NzRUL3¡!¡!" });
expect(wrapper.state('bar')).to.equal("K1TT3NzRUL3¡!¡!");
expect(wrapper.props('foo')).to.equal("gbenavid");
});
});

Note: If you would like to test componentDidUpdate()establish your wrapper like so:

const wrapper = shallow(<MyComponent />, { lifecycleExperimental: true });

Simulate

This one is really cool. Enzyme has a way for you to simulate events. I’m mounting here, but you can also do a shallow rendition. This is helpful for hitting those methods that only execute on some event, i.e onClick(), onSubmit()… etc. Occasionally you must mount if you’re testing DOM APIs and/or refs (more on that later).

describe("Foo Component", () => {
it("textarea hits method handleBlur", () => {
const wrapper = mount(<Foo Component/>);
wrapper.find("textarea").simulate("blur");
// simulate also supports 'change', 'focus', 'submit' among others.
// make assertion here.
});
});

Simulate also takes an optional argument as an event. How to pass object with event(s):

let e = {target : { value: "some value" }}; wrapper.find("textarea").simulate("blur", e);

Testing refs & simulating clicks.

// In your .jsx file
<textarea
ref="input"
className="reusable-input"
style={this.state.inputStyle}
value={this.props.value}
onChange={this.handleChange}
onBlur={this.handleBlur}
onFocus={this.handleFocus}
onKeyDown={this.handleKeyDown}
/>

I could test any of the above functions like this with chai simulations.

let spy = chai.spy.on(Foo.prototype, "someFunction");
const wrapper = mount(<ReusableTextArea {...props}/>);
// Again, if you have a function that hits a DOM API then you need to mount
wrapper.find("textarea").simulate("blur");
expect(spy).to.have.been.called.once();

Making assertions!

Chai.js: (assertion library for enzyme)

Next you’ll learn how to make assertions, and manipulate your rendered components. So far we’ve only traversed and rendered jsx. Now we’ll brush over some basic functionality provided by Chai, and a plugin called chai-spies. Hopefully this will give you a better foundation before you dive into the docs. Also chai-spies has a handful of chain-able getters that I won’t directly cover, but you can do a quick reference of all of the available ones in the docs.
The spy interface looks like this:chai.spy(<Obj>, <[method]>)

I’ve used it in the above examples, but here is where I’ll explain how to implement it. Spy on a method in component Foo

// to use this pluggin add this to the top of your testing file
const chai = require("chai"), spies = require("chai-spies");
chai.use(spies);
import Foo from "./<path to component>/Foo.jsx";
describe("Foo", () => {
it("a call to handleBlankSpace will not error", () => {
let spy = chai.spy(Foo.prototype, "componentWillRecieveProps"); // spy
const wrapper = mount(<Foo/>); // est. rendition of <Foo/>
wrapper.setProps({bar: "baz"}); // manipulate you component in some way
expect(spy).to.have.been.called.once(); // assert expectations/test
});
});

Making assertions based on props and state.

// in jsx file
this.state= {
bar: "red"
}
let changeState = function () {
this.setState({bar: "blue"});
}
// in testing file:
let props = {
...
}
it("handleChange will assert new state", () => {
const wrapper = shallow(<Foo {...props}/>); // spread obj passes in props obj.
wrapper.instance.changeState();
expect(wrapper.state("bar")).to.equal("blue");
});

Testing inner functions.

// in your jsx file that exports component Foo
let parentFunction function() {
this.childFunction();
}
// in your test file
it("an example of testing inner functions", () => {
const wrapper = shallow(<Foo {...props}/>);
let spy = chai.spy.on(wrapper, "childFunction");
wrapper.instance().parentFunction();
expect(spy).to.have.been.called.once();
});

SetTimeouts (and other mocha rich features): Mocha is responsible for all of the asynchronous testing functionality. There’s a ton of cool functionality that can be done with mocha, but to start setTimeout is one that I found particularly helpful.

it("some test that uses setTimeout", (done) => {
// set the scope of done by passing it as an arg to your it block
const wrapper = shallow(<Foo />);
let spy = chai.spy.on(wrapper, "childFunction");
wrapper.instance().parentFunction();
setTimeout(() => {
expect(wrapper.state("foo")).to.equal("lorem");
done();
}, 100);
});

If done() isn’t called, then your test will error saying you’ve exceeded the time limit.

The second argument that setTimeout() takes is a number. The above example uses 100. But you’ll have to find this number in your .jsx file under the function your testing. It’ll control how long your test will wait.

Gotchas

Make sure that you’re test is passing when you expect it to pass and breaking when you expect it to break.
Here are a few gotchas that I’ve found. Feel free to add to this section as you encounter new ones.

to.be.called.once // don't have evoke for stylistic flare to.be.called() // MUST be evoked otherwise the test will ALWAYS pass.

If you’re testing immutable objects, make sure you make your assertion like so:

to.deep.equal({foo:'lorem'});

Related articles

Enzyme API Docs
Enzyme | Airbnb Engineering

--

--