Implementing basic Component tests using Jest and Enzyme

Written by lukepierotti | Published 2017/11/13
Tech Story Tags: javascript | jest | enzyme | test-driven-development | react

TLDRvia the TL;DR App

This is the first of maybe a couple of posts that I am going to be doing on testing React components and TDD in general. This one deals with setting up the Jest and Enzyme libraries and then writing a couple basic tests for a Login component. In my next posts hopefully I’ll go into a little more detail about these libraries and about how we can test more complex components that involve Redux. So with that let’s get started!

What is Jest?

Jest is a testing library that can be used to test simple Javascript code or React components. This is done through simple API that Jest provides to users. Using this API it is possible to make assertions on how functions should behave and then test our expected outcome against the test outcome. It is very easy to set up and is usable right out of the box. Thats why its the preferred testing library for Facebook. A cool feature that Jest has is snapshots. This takes a snapshot of your component and how it renders and then will compare that whenever doing other tests to let you know if something unexpected is happening. I won’t go into too much detail on snapshots because I’ll cover those in another post. When using Jest with Enzyme it is possible to test components through a mock DOM.

What is Enzyme?

Enzyme is another library commonly used with either Jest or Mocha and Chai. With Enzyme we can create a mock DOM to test whether components are rendered correctly, and whether they behave correctly when acted upon. Enzyme’s mock rendering can either be done through shallow rendering or full DOM rendering.

Shallow rendering is used when doing unit testing of a component. This allows for tests to focus only on that component and how it functions, without caring about other components it might interact with.

Full DOM rendering involves rendering your component and all children components. The allows for more in-depth testing to see how your components on the DOM interact with each other.

Setting up Jest and Enzyme

First lets add both Jest and Enzyme to our React app. To install Jest simply run this code,

npm install --save-dev jest

Chance are it is already installed if you used Create React App to build the base of your application. Enzyme requires a couple of packages to be installed. First we’ll install Enzyme and the proper adapter to go with it. We must specify which version of React we are using to get the correct testing adapter. I am using React 15.6.1 so the packages I’ll be installing are

npm install --save-dev enzyme enzyme-adapter-react-15

The full list of adapter packages can be found here. Next we will install the Jest Enzyme package so we’ll be able to use them together like this,

npm install --save-dev jest-enzyme

Now that we have both of the libraries installed we have to set up a couple more things. To begin lets set up our file structure. It is usually best to keep your component tests close to the component code. So lets create a subfolder called __tests__ in our components folder. This is where we will create a file for each component to write our tests.

One last thing we need to set up is our adapter. In order to use Enzyme with React, we need to create another file and tell Enzyme what adapter to use depending on our version of React. This is where we will use that package we installed earlier, that corresponded to our React version. Because I used Create React App we can create a file called setupTests.js in the src folder. The code would look like this

// in app/src/setupTests.js file

const Enzyme = require('enzyme');

// this is where we reference the adapter package we installed// earlierconst EnzymeAdapter = require('enzyme-adapter-react-15');

// This sets up the adapter to be used by EnzymeEnzyme.configure({ adapter: new EnzymeAdapter() });

Time to Start Testing

Now that we have everything set up we can begin writing our tests. The component we’ll test is a simple Login component. We will create our test file in our __tests__ folder that we made earlier. It should look like this,

// path with the example test file nameapp/src/components/__tests__/Login-test.js

If you are new to both of these libraries, like myself, I suggest looking at their documentation in order to determine the correct API methods to use for your tests. The first thing we have to do is import our component, React and a couple of functions from Enzyme. We’ll import shallow, mount, and render from Enzyme because these will mock our component rendering in the DOM. We will only use shallow for now, because we don’t need to test interaction between this component and others.

// Login-test.js

import React from 'react';import { shallow, mount, render } from 'enzyme';import Login from '../Login';

So normally in Test Driven Development (TDD) we write tests first, then write the function or component to make these tests pass. But since this is an example, I’ll give the sample code for the component to see how we select parts of the code to test.

// app/src/components/Login.js

import React from 'react';

class Login extends React.Component {

constructor() {super()this.state = {username: '',password: ''}}

handleInputChange = (event) => {this.setState({[event.target.name]: event.target.value})}

render() {return (<form className='login'><label>Username</label><input id='email' onChange={this.handleInputChange} name='email' type='text' /><label>Password</label><input id='password' onChange={this.handleInputChange} name='password' type='password' /><button>Submit</button></form>)}}

export default Login

So first we write our describe functions to describe what we are testing, then we write our assertions about the behavior of certain parts of the component. This test checks to see if our component renders to the DOM properly,

import React from 'react';import { shallow, mount, render } from 'enzyme';import Login from '../Login'

// describe what we are testingdescribe('Login Component', () => {

// make our assertion and what we expect to happenit('should render without throwing an error', () => {expect(shallow(<Login />).find('form.login').exists()).toBe(true)})})

This test is shallow rendering our Login component, then we check to see if our form node exists in the rendered HTML. Now that we know the form renders successfully, we can add a couple more simple tests to make sure that the inputs on our form render correctly too.

// within the Login components describe function

it('renders a email input', () => {expect(shallow(<Login />).find('#email').length).toEqual(1)})

it('renders a password input', () => {expect(shallow(<Login />).find('#password').length).toEqual(1)})

In both of these tests we are using the find function provided by Enzyme to find our inputs using their ids. Since find returns all the nodes that match our search terms we are just checking to make sure there is one of each input. We can simply check the length to see if the node exists.

Now lets move on to simulating input changes. This is a controlled form so we can test when an input is changed, to see if it successfully changes the state of our Login component. To do this we’ll use the simulate function provided by Enzyme, and pass in as the second argument our simulated event object. The test will look like this,

// within the Login components describe function

describe('Email input', () => {

it('should respond to change event and change the state of the Login Component', () => {

const wrapper = shallow(<Login />);wrapper.find('#email').simulate('change', {target: {name: 'email', value: '[email protected]'}});

expect(wrapper.state('email')).toEqual('[email protected]');})})

describe('Password input', () => {

it('should respond to change event and change the state of the Login Component', () => {

const wrapper = shallow(<Login />);wrapper.find('#password').simulate('change', {target: {name: 'password', value: 'cats'}});

expect(wrapper.state('password')).toEqual('cats');})})

So what happens when we call shallow on our Login component, is that instead of returning the rendered component, it returns a wrapped version of the component. That is why I set the value of wrapper to the shallowly rendered Login component. Next, we simulate a change event called on our inputs with our mocked event data. The way the simulate function works is it doesn’t actually simulate the event. It instead looks for the prop corresponding to that event on the selected node, in this case onChange, and calls it passing our mock event data.

After we simulate the event we check the overall Login component’s state. We make sure that the onChange function was called properly and behaved properly, after the input was changed.

With all these tests written like this, the output in your terminal should look like this,

Summary

These examples have just been some simple tests that you can write using Jest and Enzyme. The most important parts of these examples are the structure of them. Whenever writing tests, no matter what testing libraries are used, it is always important to structure them properly. This means organizing your tests logically under describe functions to focus on one part of the component at a time. Notice how I included a describe function for both my inputs, in order to make it clear that those tests focus on those specific parts of the component. This makes tests easier to understand and read for other programmers. Hopefully now you have an understanding of how to install these libraries, and how to start writing basic tests for your components! Check out the resources below for more documentation on what functions Jest and Enzyme allow you to use!

Check out my next post to learn about Snapshot testing with Jest. For information on how to test Redux connected components check out my new post.

Sources:

airbnb/enzyme_enzyme - JavaScript Testing utilities for React_github.com

ShallowRenderingAPI_enzyme - JavaScript Testing utilities for React_github.com

Jest · 🃏 Delightful JavaScript Testing🃏 Delightful JavaScript Testingfacebook.github.io


Published by HackerNoon on 2017/11/13