Testing your first React Component with Jest and Enzyme
Richard Igbiriki
Posted on September 21, 2019
Introduction
If you've spent three months writing code in recent times then you must have come across the concept of 'testing' or 'test-driven development' (TDD). It is considered a best practice to test your code to ensure that it works as you expect it to work under any circumstance. In my experience, testing, for a lack of a better word, 'forces' us to code better (think readability, scalability).
Writing testable code is a skill in itself. Unit testing, for instance, requires splitting React components into smaller (preferably, pure components) components.
Testing Environment Setup
create-react-app
sets up React with jest as the default testing library. To enable us to test react components, we need to add enzyme
to our project's development dependencies.
yarn add enzyme enzyme-adapter-react-16 --dev OR npm install enzyme enzyme-adapter-react-16 --save-dev
Add setupTests.js
to your src
directory. Add the setup code below to it.
// /src/setupTests.js
import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
configure({ adapter: new Adapter() });
Jest automatically finds test files that have the suffix *.test.js
or *.spec.js
. Another option is to place all test code into a _tests_
folder. Jest will find and run all test files regardless of relative directory position to src
.
Syntax
As I like to put it, tests are just conditionals. True OR False. A binary comparison between an actual and expected value. For instance, we expect 1 to equal 1. Or we expect 1 + 1 to equal 2. This is precisely how we test our components. By comparing an expected outcome to the actual outcome.
Using jest we:
expect(actual).toEqual(expected);
OR
expect(actual).not.toEqual(expected);
Enzyme provides a list of Matchers and Jest provides methods of comparisons for expect
Testing Our Component
Below is the component we will be testing
We will keep our tests for this component simple...for brevity (or so I claim).
For this component, I want to test:
- Presence of input with email id
- Presence of input with password id
- Presence of an avatar icon using the
avatar
class. - Presence of a button with 'Login' text.
- The validation function that ensures email and password is not empty.
Let's get started!
Tests
Here is our Login.test.js
import React from 'react';
import { shallow } from 'enzyme';
import Login from './Pages/LogIn';
import { validateEmailAndPasswordPresence } from '../extra/validations';
describe('Login component tests', ()=> {
const wrapper = shallow(<Login />);
it('should have a btn component', ()=> {
//There should be only one button
expect(wrapper.find('Button')).toHaveLength(1);
//Button should be of type button
expect(wrapper.find('Button')
.type().defaultProps.type)
.toEqual('button');
//Button should have matching text
expect(wrapper.find('Button').text()).toEqual('LOGIN');
});
it('should have input for email and password', ()=> {
//Email and password input field should be present
expect(wrapper.find('input#email')).toHaveLength(1);
expect(wrapper.find('input#password')).toHaveLength(1);
});
it('should an avatar', ()=> {
//Avatar should be present
expect(wrapper.find('.avatar')).toHaveLength(1);
});
it('should have an empty email and password state var', ()=> {
//Optionally test to check if password and email are empty strings on
setup
expect(wrapper.state('email')).toEqual('');
expect(wrapper.state('password')).toEqual('');
});
it('should test email and password presence', () => {
//should return true
expect(validateEmailAndPasswordPresence('email@email.com',
'password').toEqual(true);
//should return false
expect(validateEmailAndPasswordPresence('',
'').toEqual(false);
});
});
Note: The button tested here is the button component from react-bootstrap
. Replace with your own button component.
Explanation
describe
is used to describe what we are about to test. It is a wrapper function for tests concerning a particular component.it
describes a particular test. I useit
as a should (comparison). Soit
should have an avatar. Orit
should be true if email and password present.
To run your test, use
npm test or yarn test
in your project directory. All tests should fail. Yes!!! The joy of TDD.
Login Component
import React, {Component} from 'react'
import {FaUserCircle} from 'react-icons/fa'
import {Form, Button} from 'react-bootstrap'
import { Redirect } from 'react-router-dom'
export default class Login extends Component {
constructor() {
super();
this.state = {
email: '',
password: ''
}
}
handleChange = (event) => {
this.setState({[event.target.name]:event.target.value});
}
login = async () => {
//login here
}
render () {
return(
<div className="container-fluid box mt-5">
<div className="loginbox shadow p-5">
<FaUserCircle className="avatar" />
<Form className="p-2 mt-5">
<Form.Group className="mb-3">
<Form.Control id="email" type="text" name="email" onChange={this.handleChange} placeholder="Email/Username" className="text-center" />
</Form.Group>
<Form.Group className="mb-3 mt-4">
<Form.Control id="password" type="password" name="password" onChange={this.handleChange} placeholder="Password" className="text-center" />
</Form.Group>
<Form.Group className="Log-button">
<Button onClick={this.login} className="btn" style={{width: '10rem'}}>LOGIN</Button>
</Form.Group>
</Form>
</div>
</div>
)
}
}
After installing all the dependencies using yarn' or
npm`, rerun your tests and they should all PASS.
Disclaimer: There are more tests in the image above than covered here.
Conclusion
Test-Driven Development (TDD), Behaviour-Driven Development (BDD), Poverty-Driven Development (PDD), CLI-Drive Development (CDD), and whatever else exists all lead to one thing, a product. What matters is that we follow patterns and use best practices that suit our current development circle.
I have been coding for a couple of years and just started writing tests, although I do wish I started earlier. If you do not think TDD is worth it, give it a few more days or months or years, see you when you finally decide.
Posted on September 21, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.