Beginners' Introduction to React Testing

Being a software developer the need to write code and deploy working code to production quickly and safely is needed especially on products that have crazy deadlines and delivery schedules. And it's a known fact that things do break!

No matter the many years of experience one has things get complicated and a change made at some part of the code base like the way a function appends data to an object or processes a text could cause a bug in the system that could take days before an engineer can fix it, the need to monitor our codebase to make sure changes made to it do not affect the way the code base works currently is why we have tests.

Intro to testing

Creating software to solve problems or perform a task in a structured way is software engineering, creating/writing tests is a way to make sure that the programs developed work in that structured way and do not break regardless of the changes made to the codebase over time, it's a good way of being sure that different developers working on a single code base don’t create problems in the codebase.

Why testing

Apart from the obvious reasons stated above, the writing test has a lot more advantages in the software development cycle.

  • Helps you see issues with your codebase:

I've had occurrences that after working on my code base and I start writing my test, I realize that my function wasn't implemented in the best possible way and would be prone to errors in the future.

  • Saving time on debugging

Running tests helps you make sure your codebase is working before entering production; this can save a lot of time in development.

  • Confident your code won't break on production

Test Cases

A test case is a set of commands executed by software to verify that a particular function or feature is working correctly the way it was designed to function, this could contain test data, and conditions developed for that particular scenario or feature.

What to test

So what is meant to be tested? Basically, a test should be run to check the functionality of the app, building on our previous point. Running the test should give you the confidence that the app still functions the same way it intended while pushing to production, even after making changes to different parts of it.

Testing in React

Testing in React is done by making use of libraries, one of them being called ReactTestUtils, it makes it easier to test React components.

Example

So let's look at a little example by testing a Button Component and see if it works.

A simple Component

import React from 'react'

export default function Button() {
   return (
     <Button>
         Hey i'm alive
     </Button>
   )
}

Now to test the component we have to create our test file.

Opening up our folder root directory

mkdir test && cd test && touch simple.test.js

The above command creates our test file.

Now we paste the following inside our file

Test Scenario 1

This simple test is meant to check if our component was rendered perfectly

Simple.test.js

import Button from './components/button';
import { render, screen } from '@testing-library/react';
test('render the button Component', () => {
  render(<Button />);
  const linkElement = screen.getByText('Hey i am alive');
  expect(linkElement).toBeInTheDocument();
});

The test above checks if the component can be rendered by checking if the component contains some preset Text.

Let's break down the code.

import Button from './components/button';
import { render, screen } from '@testing-library/react';

Importing the needed tools we need from our library plus the component we are testing.

test('render the button Component', () => {

....

});

This part of the code is where we describe our test and what it does, and in the callback function, we declare our codebase.

const linkElement = screen.getByText('Hey i am alive');
 expect(linkElement).toBeInTheDocument();

This part of the code gets the content from the page and checks if that content is what we are expecting.

Running

npm run test

Generates this, here we can see our test has been running and we can count it as passing or passed.

Test Scenario 2

Now we have been able to write our first test to see if a component has rendered, now we can move up and test components that have a bit of logic inside them.

Our next example involves us passing two variables and setting the content to the sum of the two numbers.

Now let's make the edits to our components.

import React from 'react';

export default function Button({ number1, number2 }: any) {
 const [total, setTotal] = React.useState(0);
 return (
   <div>
     <button
      data-testid='button'
       onClick={(e) => {
         setTotal(number1 + number2);
       }}
     >
       Adds 2 number {number1} and {number2}
     </button>

     <p data-testid='total' > The total is : {total} </p>
   </div>
 );
}

Now we can test the component to see if the Button Component works the way it was meant to.

Let's go through our codebase and see what each line does.

const component = await render( <Button number1={3} number2={3} />);

We declare our component and set the number1 and number2 props.

const total =  component.queryAllByTestId('total')[0]

We need to get the object of the

tag with the testId

const button =  component.queryAllByTestId('button')[0]

We need to get the object of the button tag with the testId

button.click()

We need to simulate the click event by calling the click function

expect(!!getByText(total, `The total is : ${3+3}`, { exact: false }) ).toBe(true)

Now after clicking the button we are gonna expect the text to be The total is 6.
The test passing means that the code works the way it's meant to.

What if something changes

John is a new engineer coming into the company and feels like making some changes to the codebase lets see what he does.

import React from 'react';

export default function Button({ number1, number2 }: any) {
 const [total, setTotal] = React.useState(0);
 return (
   <div>
     <button
      data-testid='button'
       onClick={(e) => {
         setTotal(number1 - number2);
       }}
     >
       Adds 2 number {number1} and {number2}
     </button>

     <p data-testid='total' > The total is : {total} </p>
   </div>
 );
}

Now we change

setTotal(number1 + number2);

To

setTotal(number1 - number2);

Let's see how that affects our codebase

Now we see that our test is failing because our new engineer changes make the logic of the codebase faulty.

So changes are made to the codebase to revert it to its previous version and the test runs again

Conclusion

Let's walk through what we’ve learned so far, testing is programmatically checking if the code base still works after changes made to our codebase by either us or other engineers.

Testing with react is done by tools that make it easier to perform this test, we simply mock the app functionality and expect it to create an expected result or outcome doing this ensures our code works perfectly.

Originally written by King Somto for JavaScript Works

14