Testing your first React Component with Jest and Enzyme

richardigbiriki

Richard Igbiriki

Posted on September 21, 2019

Testing your first React Component with Jest and Enzyme

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

Enter fullscreen mode Exit fullscreen mode

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() });

Enter fullscreen mode Exit fullscreen mode

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); 

Enter fullscreen mode Exit fullscreen mode

OR

expect(actual).not.toEqual(expected); 

Enter fullscreen mode Exit fullscreen mode

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

Alt Text

We will keep our tests for this component simple...for brevity (or so I claim).

For this component, I want to test:

  1. Presence of input with email id
  2. Presence of input with password id
  3. Presence of an avatar icon using the avatar class.
  4. Presence of a button with 'Login' text.
  5. 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);
        });

    });

Enter fullscreen mode Exit fullscreen mode

Note: The button tested here is the button component from react-bootstrap. Replace with your own button component.

Explanation

  1. describe is used to describe what we are about to test. It is a wrapper function for tests concerning a particular component.

  2. it describes a particular test. I use it as a should (comparison). So it should have an avatar. Or it should be true if email and password present.

To run your test, use

npm test or yarn test

Enter fullscreen mode Exit fullscreen mode

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>
        )
    }
}

Enter fullscreen mode Exit fullscreen mode

After installing all the dependencies using yarn' ornpm`, rerun your tests and they should all PASS.

Alt Text

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.

💖 💪 🙅 🚩
richardigbiriki
Richard Igbiriki

Posted on September 21, 2019

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related