Peter Ekene Eze Learn, Apply, Share

Testing React applications in 2019

6 min read 1680

testing in react

Testing is a very controversial concept in software development. While it is difficult for everyone to agree on the best ways to go about testing or the best tools, or even the level of priority to accrue to testing, what we can all agree on is it is a very critical aspect of any product and it should be treated as such.

In this post, we’ll take a closer look at some of the best ways you can go about testing your React applications. The concepts we will explain here will obviously apply to other JavaScript frameworks like Vue or even other languages however, for preciseness, we’ll make our demonstrations in React.

Before we get into it, it is worthy to note that this post is not a comprehensive introduction to testing. It is more of an eye-opener to the ways you should be going about testing in React (if you aren’t already).

Prerequisites

Before we go any further, this article assumes the following:

  • Node.js ≥v6 is installed on your machine
  • npm is installed on your machine
  • React version 16.8 or above installed on your machine
  • Create-react-app installed on your machine
  • You have a basic understanding of ReactJS

General testing concept

If you are completely new to the concept of testing, think of it like this — testing is a way for you to automate the activities of your application without having to manually check if each function in a component does what it is supposed to do. Of course, this is not all there is to testing but it gives you a general idea to start.

Tests equally help with code moderation. If you have multiple contributors working on the same project, testing can help you specify the exact piece of functionalities for the individual parts of your codebase. As a result, it becomes quite easy to detect a problem in a system and proffer a fix.

JavaScript testing frameworks

To date, Jest remains arguably the most popular JavaScript framework with over 27k stars on Github. It was built by Facebook and it continues to be maintained and supported by the Jest team at Facebook. Jest is a zero-configuration javascript testing framework recommended by React and it is quite easy to use. It has a very impressive acceptance rate in 2019 by the JavaScript community with over 900 contributors.

The other popular alternatives are Mocha and Jasmine. Mocha claims to be the most used JavaScript testing framework. It has over 18k stars on Github. Asides the massive ecosystem Mocha has well-established options with a great documentation. It is also very flexible and open to a lot of extensions.

Jasmine, on the other hand, has proven to be the officially recommended testing framework for Angular.js. It has over 14k stars on Github and it is also one of the oldest testing frameworks with the most resources and community support. Even Jest was built on Jasmine.

Having considered these frameworks, it is worthy to note that there’s no explicit “best”. In the long run, it all comes down to what’s best for you. In this post, we will be using Jest for our demonstrations.

Configuring Jest

By default, create-react-app comes with these configurations. However, for flexibility and completeness, we demonstrate how to manually configure Jest with webpack for the client side.

Step 1: Run npm install --save-dev jest on your project directory
Step 2: Head over to the package.json file in your application and add a test script:

"script":{
  "test": "jest"
}

Step 3: Next, we would have to set up the .babelrc.js file because we have a preset in the package.json pointing to it. Jest automatically picks up the file and applies it to all of our tests

const isTest = String(process.env.NODE_ENV ) === 'test'
module.export = {
  presets: [['env', {modules: isTest ? 'commonjs' : false}], 'react'],
  plugins: [
  'syntax-dynamic-import',
  'transform-object-rest-spread',
],
}

With this, babel can now recognize that we are passing tests then transpile all our ESmodules to CommonJS.

Testing React applications

There are a number of ways to test React applications. We are going to look at a few of them.

Unit testing React components

Unit testing involves testing individual units/components of a software in isolation to verify its correctness. Now how do we achieve this in a React application? If we have a login component in a login.js file like so:

function Login({ onSubmit }) {
  return (
    <div>
      <Form
        onSubmit={e => {
          e.preventDefault()
          const { username, password } = e.target.elements
          onSubmit({
            username: username.value,
            password: password.value,
          })
        }}
      >
        <label style={{ justifySelf: 'right' }} htmlFor="username-input">
          Username
        </label>
        <Input
          id="username-input"
          placeholder="Username..."
          name="username"
          style={{ flex: 1 }}
        />
        <label style={{ justifySelf: 'right' }} id="password-input">
          Password
        </label>
        <Input
          placeholder="Password..."
          type="password"
          name="password"
          aria-labelledby="password-input"
        />
      </Form>
    </div>
  )
}

The above code is a simple login component we would be testing in a login.test.js file.

import React from 'react'
import ReactDOM from 'react-dom'
import Login from '../login'
  test('calls onSubmit with the username and password when submitted',() => {
    const handleSubmit = jest.fn()
    const container = document.createElement('div')
    const form = container.querySelector('form')
    const {username, password} = form.element 
    username.value = 'Kenny'
    passwords.value = 'pineapples'
    
    form.dispatchEvent(new window.event('submit'))
      expect{handleSubmit}.toHaveBeenCalledTimes(1)
      exopect{handleSubmit}.toHaveBeenCalledWith({
        username: username.value,
        password: password.value, 
      })
  ReactDOM.render(<Login onSubmit = {handleSubmit} />, container)
  })

The test looks for a div and passes it into a container variable. Then from that container variable, we create a form by calling the querySelector('form') on it.

Next, we use object destructing to get the fields from the form.element. Because called dispatchEvent() on the submit event, we can test for what we aspect the form to do or what value it should have when the submit event is fired. This shows that the event should be fired once and should have the username and password when fired.

form.dispatchEvent(new window.event('submit'))
  expect{handleSubmit}.toHaveBeenCalledTimes(1)
  exopect{handleSubmit}.toHaveBeenCalledWith({
  username: username.value,
  password: password.value, 
})

And of course, we can run the test with npm run test.

Snapshot testing

Previously we were able to test a specific component to ensure that they act like they’re supposed to, but one thing we haven’t done yet is test for the structure of the user interface. We can do that with snapshot testing. Consider the example below:

render(){
  <div>
    <p> Current count: {this.state.count}</p>
    <button className = 'increment'
      onClick ={this.increment}>
        + 
    </button>
    <button className = 'decrement'
      onClick ={this.decrement}>
        -
    </button>
  </div>
}

Imagine if a component had a specific format, like an increment button coming before a decrement button and the tests pass when this is true. If a designer changes this format, it would in-fact change the format of rendering to the DOM. So how do we avoid accidental changes to the render function of the DOM.

A snapshot test helps you take a snapshot of a component at a given time and store what it rendered previously on the DOM. So, when you run the test for the component, Jest will let you know if what you rendered is different from the snapshot it has already. You can either accept the change or be alerted to the change.

To carry out this test we will be using the react-test-renderer form, which will give us a JSON representation of our test at a specific time. We will then store that data with Jest:

import React form 'react'
import Counter from './counter'
import {shallow} from 'enzyme'
import renderer from 'react-test-renderer'

describe('Counter component', () => {
it('matches the snapshot', () => {
  const tree = renderer.create(< Counter/>).toJson()
expect(tree).toMatchSnapshot()
})
it('start with a count of 0', () => {
  const wrapper =shallow(<Counter/>)
  const text = wwrapper.find('p').text()
  expect(tesxt).toEqual('Current count: 0')
})
it('can increment the count when the button is clicked', ()=>{
const wrapper = shallow(<Counter/>)
}

First, we get a JSON representation of the counter component that will be stored in Jest. The expect``() method takes the tree as an argument and that is what causes the comparison with the next re-rendering.

Integration testing

As previously stated, integration testing is where individual units are combined and tested as a group. For example, if we had two functions working together within a single context, we would use an integration test to make sure they interact properly with each other. Let’s consider the simplest use case — add two numbers together in a component.

export const add = (x,y)=> x + y

export const total = (Tax,price) => {
  return "$" + add(Tax, price)
}

Then in the app.test.js we do:

import {add,total} from './App' 

test('add', () => {
  expect(add(1,2)).toBe(3)
})

test('total', () =>{
  expect(total(5,20)).toBe(25);
})

A recommended testing tool

React-testing-library

Personally, I think this is a great tool for testing React components. It addresses testing from a users perspective. It is also really helpful because it works with specific element labels and not the composition of the UI. To demonstrate how this library works, let’s refactor the previous unit test we wrote using this library.

import React from 'react'
import ReactDOM from 'react-dom'
import {render,simulate} from 'react-testing-library'
import Login from '../login'

test('calls onSubmit with the username and password when submitted',() => {
const fakeuser = generate.loginForm
const handleSubmit = jest.fn()
const {container,getByLabelText, getByText} = render(<login onSubmit= {handleSubmit}/>)
const usernameNode = getByLabelText('username')
const passwordNode= getByLabelText('password')
const formNode = container.querySelector('form')
const submitButtonNode = getByText('submit')

In the example above, we focused more on testing elements by getting the name associated with them rather than being bothered with the UI. This is a major advantage of using this library over other alternatives like enzyme and cypress.

Conclusion

In this post, we have looked at various methods of testing React applications and the importance of testing. I hope this post helps you understand the importance of testing in React and shows you the ways you can go about it.

Plug: , a DVR for web apps

LogRocket is a frontend logging tool that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.

.
Peter Ekene Eze Learn, Apply, Share

Leave a Reply