Testing your first React Component with Jest and Enzyme
Tags: react, testing, javascript, tdd
Published: 2019-09-21 13:08:53

### 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](https://airbnb.io/enzyme/docs/api/shallow.html) and Jest provides [methods of comparisons for expect](https://jestjs.io/docs/en/expect)


### Testing Our Component

Below is the component we will be testing

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/q3nihjtl58cr44srn8g2.png)

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

    });

```

**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

```
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. 

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/anxpt45vkylnuo8m4fo1.png)

*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.