Mastering React Test Renderer for Streamlined Testing

Relia Software

Relia Software

Huy Nguyen

Relia Software

featured

React Test Renderer is a library for rendering React components into pure JavaScript objects. It’s primarily used for writing tests for your components.

Mastering React Test Renderer for Streamlined Testing

Table of Contents

React Test Renderer is a versatile solution for streamlined React component testing. It renders components as pure JavaScript, freeing them from the DOM for better maintainability. It's a versatile toolbox for snapshot testing, inspecting component behavior, and even advanced rendering techniques. Level up your testing with React Test Renderer via this article!

>> Read more about React-related topics:

What is React Test Renderer?

React Test Renderer is a library for rendering React components into pure JavaScript objects. It’s primarily used for writing tests for your components.

React Test Renderer allows you to inspect the output of your components and ensures they work correctly. It’s especially useful for snapshot testing, where you can capture the state of your UI and compare it to a reference (snapshot) to detect changes.

One of the main benefits of using React Test Renderer is that it allows you to write tests that are decoupled from the DOM. This means your tests are not reliant on the specifics of how React interacts with the DOM, making your tests more resilient to changes in React's implementation. This decoupling leads to better maintainability of your test suite.

Installation and Setup

Installation

You can install React Test Renderer using npm or yarn:

# Using npm
npm install --save react-test-renderer

# Using yarn
yarn add react-test-renderer

Dependencies 

React Test Renderer is a package for testing React components, so it naturally depends on React. Make sure you have React installed in your project:

# Using npm
npm install --save react

# Using yarn
yarn add react

Additional Configurations

There are no additional configurations needed specifically for React Test Renderer. However, depending on your testing framework (like Jest or Mocha), you might need to configure them separately.

Remember to import react-test-renderer in your test files:

import TestRenderer from 'react-test-renderer';

Basic Usage: Hands-on Approach

First, clone the example project contains React Test Renderer inside it:

git clone https://github.com/nvkhuy/examples.git

Example is inside react-test-renderer:

cd react-test-renderer

Project structure:

├── LICENSE
├── README.md
├── package-lock.json
├── package.json
├── public
│   ├── favicon.ico
│   ├── index.html
│   ├── logo192.png
│   ├── logo512.png
│   ├── manifest.json
│   └── robots.txt
└── src
    ├── BtnGroup.test.js
    ├── ProductList.test.js
    └── VisibilityToggler.test.js

Focus on BtnGroup.test.j , ProductList.test.js , VisibilityToggler.test.js files.

To install node modules:

npm install

To test specific file, for example:

npm test src/BtnGroup.test.js

The result looks like this:

 PASS  src/BtnGroup.test.js
  ✓ the className of the component includes btn-group (5ms)
  ✓ renders BtnGroup component with children (9ms)
  ✓ renders BtnGroup component with custom props (1ms)

  console.log src/BtnGroup.test.js:33
    { children: 'child' }

Test Suites: 1 passed, 1 total
Tests:       3 passed, 3 total
Snapshots:   0 total
Time:        0.557s, estimated 1s

Let’s begin with BtnGroup.test.js file, here is the code:

import React from "react";
// [ 1 ] import the React Test Renderer
import { create } from "react-test-renderer";

const BtnGroup = ({ className = "", ...rest }) => (
  <div {...rest} className={`btn-group ${className}`} />
);

test("the className of the component includes btn-group", () => {
  // [ 2 ] create component
  const component = create(<BtnGroup />);

  // [ 3 ] get "test instance" of the component
  const instance = component.root;

  // [ 4 ] get BtnGroup element by node type
  const element = instance.findByType("div");

  // [ 5 ] assert that className equals btn-group
  expect(element.props.className.includes("btn-group")).toBe(true);
});

test("renders BtnGroup component with children", () => {
  // [ 6 ] child text
  const text = "child";

  // boilerplate code, already mentioned in [ 2 - 3 ] above
  const instance = create(<BtnGroup>{text}</BtnGroup>).root;

  // [ 7 ] get element by component name
  const element = instance.findByType(BtnGroup);

  console.log(element.props);

  // [ 8 ] assert child to match text
  expect(element.props.children).toEqual(text);
});

test("renders BtnGroup component with custom props", () => {
  // generate some custom props
  const props = {
    id: "awesome-button-id",
    className: "mb-3",
    children: "child"
  };

  // boilerplate code
  const instance = create(<BtnGroup {...props} />).root;

  // get element by component name
  const element = instance.findByType("div");

  // assert if an additional className was added to existing one
  expect(element.props.className).toEqual("btn-group mb-3");
  // assert "id" prop to match passed one
  expect(element.props.id).toEqual(props.id);
  // assert "children" to match passed
  expect(element.props.children).toEqual(props.children);
});

This file tests the BtnGroup component in React. It uses the react-test-renderer library to create test instances of the BtnGroup component and then performs various assertions on those instances. Here’s what each test does:

  • Test: the className of the component includes btn-group: This test creates an instance of the BtnGroup component and checks if the className of the div element in the component includes “btn-group”. This is to ensure that the BtnGroup component is correctly applying the “btn-group” class to its root div element.
  • Test: renders BtnGroup component with children: This test creates an instance of the BtnGroup component with a child text node. It then checks if the children prop of the BtnGroup component equals the child text. This is to ensure that the BtnGroup component correctly renders its children.
  • Test: renders BtnGroup component with custom props: This test creates an instance of the BtnGroup component with custom props (idclassName, and children). It then checks if the idclassName, and children props of the div element in the BtnGroup component match the passed props. This is to ensure that the BtnGroup component correctly applies custom props to its root div element.

The react-test-renderer library is used to render React components to pure JavaScript objects without using the DOM or a native mobile environment. This is useful for running tests on components in a Node.js environment or for rendering components to strings or JSON.

In this file, the create function from react-test-renderer is used to render instances of the BtnGroup component for testing. The root property of the test instance is then used to access the root test instance of the component, and the findByType method is used to find elements in the test instance by their type.

For testing whole project:

npm test a

This is the result:

 PASS  src/ProductList.test.js
 PASS  src/VisibilityToggler.test.js
 PASS  src/BtnGroup.test.js
  ● Console

    console.log src/BtnGroup.test.js:33
      { children: 'child' }


Test Suites: 3 passed, 3 total
Tests:       6 passed, 6 total
Snapshots:   0 total
Time:        0.696s, estimated 1s

Advanced Concepts of React Test Renderer

Snapshot Testing

Snapshot testing is a technique used in modern JavaScript testing frameworks to test the UI of an application. It works by taking a “snapshot” of your UI component and saving it as a reference. On subsequent test runs, a new snapshot of the UI component is taken and compared with the saved reference. If the two snapshots do not match, the test fails.

This approach is beneficial for maintaining consistent UI because it helps to catch unintended changes in the UI. It’s also useful for reviewing changes in the UI during code review processes.

Here’s an example of how you can set up and use snapshots for component testing:

import React from 'react';
import renderer from 'react-test-renderer';
import Link from '../Link.react';

test('Link changes the class when hovered', () => {
  const component = renderer.create(
    <Link page="http://www.facebook.com">Facebook</Link>,
  );
  let tree = component.toJSON();
  expect(tree).toMatchSnapshot();

  // manually trigger the callback
  tree.props.onMouseEnter();
  // re-rendering
  tree = component.toJSON();
  expect(tree).toMatchSnapshot();

  // manually trigger the callback
  tree.props.onMouseLeave();
  // re-rendering
  tree = component.toJSON();
  expect(tree).toMatchSnapshot();
});

Debugging Rendered Output

React Test Renderer provides several methods and properties that allow you to inspect and analyze the output details of your components. For example, the toJSON() method returns an object representing the rendered tree. This object can then be used to inspect the output.

Here’s an example:

import React from 'react';
import TestRenderer from 'react-test-renderer';
import MyComponent from './MyComponent';

const testRenderer = TestRenderer.create(<MyComponent />);
const testInstance = testRenderer.root;

console.log(testRenderer.toJSON());
console.log(testInstance.findByType('div').props.className);

Advanced Rendering Methods

React Test Renderer also supports advanced functionalities like shallow rendering or using custom matchers. Shallow rendering is useful to constrain your tests to a component as a unit, and to ensure that your tests aren’t indirectly asserting on behavior of child components.

Here’s an example of shallow rendering:

import ShallowRenderer from 'react-test-renderer/shallow';

// in your test:
const renderer = new ShallowRenderer();
renderer.render(<MyComponent />);
const result = renderer.getRenderOutput();

expect(result.type).toBe('div');

Practical Applications and Use Cases

Examining Component Lifecycle Methods

React Test Renderer allows you to test the lifecycle methods of your components. By using the update() method of the TestRenderer instance, you can trigger an update lifecycle and assert on the side effects of your componentDidUpdate() method.

Here's an example:

import React from 'react';
import TestRenderer from 'react-test-renderer';
import ProjectComponent from './ProjectComponent';

const testRenderer = TestRenderer.create(<ProjectComponent />);
const testInstance = testRenderer.root;

testRenderer.update(<ProjectComponent newProp={true} />);

expect(testInstance.findByType(ProjectComponent).props.newProp).toBe(true);

Testing React Hooks

The React Test Renderer provides a way to examine the lifecycle methods of your components. By invoking the update() function on the TestRenderer instance, you can initiate an update lifecycle. This allows you to verify the side effects of your componentDidUpdate() method.

Let’s look at an example:

import React, { useState } from 'react';
import TestRenderer from 'react-test-renderer';

function ProjectComponent() {
  const [count, setCount] = useState(0);

  return (
    <button onClick={() => setCount(count + 1)}>
      Count: {count}
    </button>
  );
}

const testRenderer = TestRenderer.create(<ProjectComponent />);
const testInstance = testRenderer.root;

testInstance.findByType('button').props.onClick();

expect(testInstance.findByType('button').children[1]).toBe('1');

>> Read more: Mastering React Hooks: Fundamentals for Beginners

Testing Event Handlers

React Test Renderer provides the capability to examine the event handlers within your components. It enables the simulation of events through direct invocation of these handlers and verification of the resulting side effects.

Let’s look at an example:

import React from 'react';
import TestRenderer from 'react-test-renderer';

function ProjectComponent() {
  return (
    <button onClick={() => console.log('clicked!')}>
      Clicking! 
    </button>
  );
}

const testRenderer = TestRenderer.create(<ProjectComponent />);
const testInstance = testRenderer.root;

testInstance.findByType('button').props.onClick();

>> Read more about testing:

Conclusion

React Test Renderer has been a valuable tool for testing React components. It offers a simple and efficient way to create snapshots of your components, allowing for easy comparison and detection of changes over time. However, it’s important to remember that testing should focus on the output of your components, not their internal implementation.

While React Test Renderer has served us well, it’s worth noting that as of 2024, there are newer and more powerful tools available for testing React applications. These include:

  • Jest: A comprehensive testing framework that works well with React.
  • React Testing Library: Encourages writing tests that closely resemble how your React components are used.
  • Enzyme: Provides more flexibility in how you can test your components.
  • Cypress: End-to-end testing framework that makes it easy to set up, write, run and debug tests.

Each of these tools has its own strengths and weaknesses, and the best choice depends on your specific needs and the nature of your project. As always, the key to effective testing is to write clear, maintainable tests that accurately reflect the behavior of your components.

>>> Follow and Contact Relia Software for more information!

  • coding
  • Mobile App Development