How to use React Test Renderer to test React components

How to use React Test Renderer to test React components

Test Driven Development (TDD) with React Test Renderer: Find out how to use React Test Renderer to test React components

It’s no secret that Enzyme has become the de facto standard for React components testing, but there are other good options around.

For example: React Test Renderer.

I personally like Test Renderer because of the way it works–it renders React components into pure JavaScript objects that are easy to use and understand.

Another advantage of React Test Renderer is that it is maintained by a core team at Facebook and is always up-to-date.

React Test Renderer has a great documentation, so I won’t duplicate it. Instead, I’d like to illustrate a few of the most common use cases in an example with a Test Driven Development (TDD) approach.

Setup

Test Renderer has a really easy setup process–just install the lib and you’re ready to go:

npm install --save-dev react-test-renderer
Testing with TDD

Ordinarily we’d need a component in order to start writing a test, but React Test Renderer enables us to write a test before the component is implemented.

Side Note: The reason for this is that TDD works like a charm when you test functions, so taking into account that most of the React components are pure functional components, TDD is applied really well here, especially with React Test Renderer. Sometimes it’s even faster to write your component starting with tests in case of complex logic because you need fewer iterations and debugging.

Let’s consider the requirements for a simple component:

  • It needs to have a class btn-group
  • It should be able to render its children
Testing className

First, we need to test the class of an empty component (as we follow TDD):

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

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

    // [ 3 ] query for element
    const element = root.findByType("div");

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

The test has 3 steps: test instance creation, element querying, and assertion.

Let’s skip over the more in-depth explanation of that for now and focus on fixing the test.
At first, it will break (as expected):

No instances found with node type: "undefined"

That means we need to add some node with some type. In our case, the type should be <div>:

const BtnGroup = () => <div />;

Once we change the code, the file watcher runs the test again and we receive an updated message:

expect(received).toEqual(expected) // deep equality

Expected: "btn-group"
Received: undefined

We’re already asserting. To pass the first test, all we need to do now is add a className prop.

const BtnGroup = () => <div className="btn-group" />;

After this change, we’ll see that rewarding green message:

As soon as the test is green we can slow down a bit and revisit the code of the test line by line. Here’s that code again:

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

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

    // [ 3 ] query for element
    const element = root.findByType("div");

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

[ 1 ] Test Renderer has only one way of creating component — the create method, so just import and use it.

[ 2 ] When creating a component, getting a test instance is a standard boilerplate code for React Test Renderer.

[ 3 ] There are 2 main ways to query for an element in Test Renderer: by type and by props. I prefer querying by type when there are no other containers around like in the current example. We’ll get to other methods a bit later.

[ 4 ] This assertion is pretty self-explanatory: just check that the ‘className’ prop value includes btn-group and you’re good to go.

Testing children

Let’s continue adding functionality to the BtnGroup component we already have since we know we need to meet the following requirement:

It should be able to render its children.

Testing the children prop is very straightforward. We just need to make sure that the passed value matches the result rendered:

import React from "react";
import { create } from "react-test-renderer";

const BtnGroup = () => <div className="btn-group" />;

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;

  // query for element
  const element = instance.findByType("div");

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

[ 6 ] The value we pass to the component and the value we use to assert against it should be the same.

Since we’re using TDD, you might expect the test to break here. However, React supports passing children to components out of the box, so our test will be green.

If you’re wondering if the test is running successfully, you can print the element value with console.log.

The output is as follows:

Testing any props

Let’s continue adding requirements for our component:

should render any props passed.

Here’s a test:

import React from "react";
  import { create } from "react-test-renderer";

  // the component is still not updated as we use TDD
  const BtnGroup = () => <div className="btn-group" />;

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

The code of the test already looks familiar: we’re just checking that the prop values match passed.

Now, the test will break and issue the following message:

Expected: "btn-group mb-3"
Received: "btn-group"

What happens now is that we need to actually start passing props. Otherwise btn-group className will always be there:

const BtnGroup = props => <div className="btn-group" {...props} />;

Here’s where having tests comes in handy. We have another message telling us that the className case is specific:

Expected: "btn-group mb-3"
Received: "mb-3"

Now, the passed props replace the props that our component already has–in our case, btn-group is replaced with mb-3.

We should change the code of the component to fix this so that it handles className differently:

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

The trick here is to de-structure props so that items needing special treatment have their name and all other props consolidated into a rest object.

Again, there is no special approach needed for the children prop, although they’re passed now as a regular prop instead of in the body of the component.

Now, the test should be green again. All of the previously written tests will also be green:

Note: I left a console.log here to show how you can check the output at any time.

As you can see, all of the assertions we’ve done — for now — are just checks that strings match.

But if there’s a need to check the number of items, we can use this handy method in Test Renderer: testInstance.findAllByType().

Let’s see how it works.

Testing the amount of items

To demonstrate how to count items in React Test Renderer, we should have some component that renders an array, or list. The requirement for it is something like this:

should render a list with correct items count.

To follow TDD, we’ll start with an empty functional component that renders an empty ul tag:

const ProductList = ({ list }) => <ul />;

Here’s a test we could write:

import React from "react";
  import { create } from "react-test-renderer";

  test("renders a list of items with correct items count", () => {
    // prepare the list for testing
    const list = [{ id: 1, text: "first item" }, { id: 2, text: "second item" }];

    // boilerplate code
    const root = create(<ProductList list={list} />).root;
    
    // [ 7 ] get list items
    const elementList = root.findAllByType("li");

    // assert if the length match with original list passed as a prop
    expect(elementList.length).toEqual(list.length);
  });

The goal of this test is to check if the number of rendered nodes equals the number of passed items.

Initially, the test will break with the following message:

To fix the test, we should render list items with li tags inside the container:

const ProductList = ({ list }) => <ul>
    {list.map(li => <li key={li.id}>{li.text}</li>)}
</ul>;

Now the test is green and we can talk about the code.

[ 7 ] To query specifically for nodes with type li, I use the testInstance.findAllByType() method that returns all elements with tag “li”.

There are also some other methods to search for multiple items: testInstance.findAll() and testInstance.findAllByProps().

The first one is useful when you need to check the overall amount, while the second one comes in handy when you want to count a specific prop, e.g., all nodes with a specific className.

Testing text

In most cases having a test for only items count is not sufficient, and you’ll also want to test the actual text a user can read.

There’s no specific functionality in React Test Renderer for that purpose, but that’s pretty easy to write if you consider that text can only be found in children.

import React from "react";
  import { create } from "react-test-renderer";

  test("renders all items with correct text", () => {
    // [ 8 ] prepare the list for testing
    const list = [{ id: 1, text: "first item" }, { id: 2, text: 33 }];

    // boilerplate code
    const root = create(<ProductList list={list} />).root;

    // get list items
    const elementList = root.findAllByType("li");

    // [ 10 ] Iterate over all items and search for text occurence in children
    elementList.forEach((el, index) => {
        // [ 11 ] convert text to string
        expect(el.children.includes(`${list[index].text}`)).toBe(true);
    });
  });

Having a list of all items in [ 8 ] we can iterate over the nodes of the component and make sure that every text was found [ 10 ].

This test is instantly green as soon as the component doesn’t have any filtering or sorting logic inside and just renders a list as it is, so we don’t have to change any lines of code in the test.

The only nit to add here is that rendered text is always a string regardless of the value type you pass [ 11 ].

Testing event handlers and hooks

Some of the functional components rely on more than just props and have their own state management thanks to the Hooks API.
Consider a classic example of a toggler component with the following requirements:

  • should render a button
  • should toggle children on button click

That means that children visibility should change on click.

Here’s an example of a test you could write:

import React from "react";
import { create } from "react-test-renderer";

// let component to be a fragment for start
const VisibilityToggler = () => <></>;

test("should toggle children nodes on button click", () => {
  const root = create(
    <VisibilityToggler>
      <div>awecome content</div>
    </VisibilityToggler>
  ).root;

  // helper to get nodes other than "button"
  const getChildrenCount = () =>
    root.findAll(node => node.type !== "button").length;

  // assert that button exists
  expect(root.findAllByType("button").length).toEqual(1);

  // query for a button
  const button = root.findAllByType("button")[0];

  // remember initial nodes count (before toggle)
  const initialCount = getChildrenCount();

  // trigger a hook by calling onClick of a button
  act(button.props.onClick);
  const countAfterFirstClick = getChildrenCount();

  // assert that nodes count after a click is greater than before
  expect(countAfterFirstClick > initialCount).toBe(true);

  // trigger another click
  act(button.props.onClick);
  const countAfterSecondClick = getChildrenCount();

  // check that nodes were toggled off and the count of rendered nodes match initial
  expect(countAfterSecondClick === initialCount).toBe(true);
});

The test looks huge, so let’s not try to fix it right away. First, let’s discuss the code a bit.

[ 12 ] Here is one new thing happens: act() method is used to wrap event handler calls.

Why should we? And how should we remember to do so? The second answer is easy: no need to remember, because React Test Renderer checks the code and prints a warning with a reason:

When writing UI tests, tasks like rendering, user events, or data fetching can be considered as “units” of interaction with a user interface.

React provides a helper called act() that makes sure all updates related to these “units” have been processed and applied to the DOM before you make any assertions ~ from the docs.

In other words, an act() method “awaits” for React updates and makes otherwise async code to look synchronous very similar to await from ES7.
At this stage, the test can’t find a button and breaks:

To resolve this issue, let’s add a button:

const VisibilityToggler = () => <><button /></>;

The button exists, but the onClick method is not found:

Don’t forget to add a button:

const VisibilityToggler = () => <><button /></>;

This is the next message you’ll receive after adding an onClick handler:

Finally, we’re at the point where we’re ready to add some state management with Hooks:

const VisibilityToggler = ({ children }) => {
  const [isVisible, setVisibility] = useState(false);
  const toggle = () => setVisibility(!isVisible);
  return (
    <>
      <button onClick={toggle}>toggle</button>
      {isVisible && children}
    </>
  );
};

Clicking on a button now toggles a state variable isVisible to the opposite value (true or false) that in return causes a render of “children” in case of “true” and skips rendering “children” in case of “false”.

All tests should be green now. You can find the complete source code for this example here:

Conclusion

Although React Test Renderer is usually associated with Snapshot testing, it can still be used to make specific assertions against your components with sufficient accuracy for most common use cases.

I personally like it because it has a clean API, it’s simple, and it’s easy to use along with TDD. I hope you like it too!

Hire React Js Developer from Expert React JS Development Company

Hire React Js Developer from Expert React JS Development Company

Are you looking to [hire React JS developer](https://www.mobiwebtech.com/react-js-development-company/ "hire React JS developer") from a prestigious and reliable React JS development company? Visit at Mobiweb Technologies here we have a big team...

Are you looking to hire React JS developer from a prestigious and reliable React JS development company? Visit at Mobiweb Technologies here we have a big team of expert React JS developers, you can hire them on an hourly, weekly or monthly basis for the successful completion of your dream project.

Learn React.js: Front-End Web Development for Beginners

Learn React.js: Front-End Web Development for Beginners

Learn React.js - Frontend Web Development for Beginners. In this React tutorial, you will learn the core fundamentals of React JS and Modern JavaScript so that you can start building lightning fast web apps using React JS. Learn Modern JavaScript and React JS from absolute scratch. Learn to make AJAX requests to get data from remote API and display into your web application. Learn React JS, which is one of the most exciting technology of recent time

Learn React.js - Frontend Web Development for Beginners - Web Development in 2019. In this video you will learn the core fundamentals of React JS and Modern JavaScript so that you can start building lightning fast web apps using React JS.

In this video, you will learn Modern JavaScript and React JS from absolute scratch. you will learn to make AJAX requests to get data from remote API and display into your web application.

So if you are ready to learn React JS, which is one of the most exciting technology of recent time

Thanks for watching

If you liked this post, share it with all of your programming buddies!

Follow us on Facebook | Twitter

Further reading

React - The Complete Guide (incl Hooks, React Router, Redux)

**☞ **Modern React with Redux [2019 Update]

**☞ **50+ Java Interview Questions for Programmers

**☞ **Top 100 Python Interview Questions and Answers

**☞ **100+ Basic Machine Learning Interview Questions and Answers

**☞ **Introduction to Java String Interview Questions and Answers

**☞ **Best 50 React Interview Questions and Answers in 2019

**☞ **Top 50+ SQL Interview Questions and Answers in 2019

**☞ **Best Java Microservices Interview Questions In 2019

**☞ **Best 50 Nodejs interview questions from Beginners to Advanced in 2019

**☞ **100+ Java Interview Questions and Answers In 2019

React Web Development: A Guide to Develop Progressive Web Applications

React Web Development: A Guide to Develop Progressive Web Applications

Outperform your competition with progressive web development and React.

Progressive web applications (PWAs) quickly gained popularity because they are web performance applications based on fast performance, streamlined to provide a mobile app-like experience. PWAs are built using HTML, CSS, and JavaScript to create a level of availability and performance equivalent to that of native mobile applications. They respond quickly, consume less data, store more space, and support push notifications and offline use in the browser.

Building a progressive web application has now become the web development trend that every business wants to follow. Significant players like Twitter and Flipboard have recently rolled out their progressive web apps to provide a mobile experience for users, without requiring them to install the app. In this article, you will learn how to build a progressive web application using React. Let's get started.

Step One — Set up the React Application

First, create a React application with create-react-app. To do so, you need to run the following commands:

  • npm install -g create-react-app
  • create-react-app pwa-app

Now, install React Router:

You need to replace the content of src / App.js using the code below to get a basic template with navigation.

import React, { Component } from 'react';
import { Router, browserHistory, Route, Link } from 'react-router';
import './App.css';

const NavBar = () => (
<div className="navbar">
<Link to="/">Feed</Link>
<Link to="/profile">Profile</Link>
</div>
);

const Template = ({ title }) => (
<div>
<NavBar />
<p className="page-info">
This is the {title} page.
</p>
</div>
);

const Feed = (props) => (
<Template title="Feed"/>
);

const Profile = (props) => (
<Template title="Profile"/>
);

class App extends Component {
render() {
return (
<Router history={browserHistory}>
<Route path="/" component={Feed}/>
<Route path="/profile" component={Profile}/>
</Router>
);
}
}
export default App;

Now, you'll have to update the default styles by replacing your src/App.css with the styles below to make your application look clean.

.navbar {
background-color: #01C8E5;
text-align: center;
}

.navbar a {
display: inline-block;
padding: 10px;
color: #fff;
text-decoration: none;
}

.page-info {
text-align: center;
font-weight: bold;
}

Then, run npm start to test the application in the browser. It is basically an application with two routes. Now, you will convert it to PWA.

Step Two — Lighthouse Setting and Auditing

Lighthouse is an automated open-source tool for testing applications against PWA checklists. It facilitates audits for accessibility, performance, and more.

Check your application with Lighthouse. Click the Lighthouse icon from the top right corner in Chrome and then click the "Create Report" button. The generated report will look like this:

Creating a report to test your PWA with Lighthouse

Fix all failed audits.

Step 3 — Sign Up for a Service Staff

Service employees are proxy servers that connect the application and the network. With Service Worker, you will have to block network requests and save cached files. It allows your application to work even with an unavailable system.

Create an empty worker.js file in your application's general directory and add the following code to that file.

// Flag for enabling cache in production
var doCache = false;

var CACHE_NAME = 'pwa-app-cache';

// Delete old caches
self.addEventListener('activate', event => {
const currentCachelist = [CACHE_NAME];
event.waitUntil(
caches.keys()
.then(keyList =>
Promise.all(keyList.map(key => {
if (!currentCachelist.includes(key)) {
return caches.delete(key);
}
}))
)
);
});

// This triggers when user starts the app
self.addEventListener('install', function(event) {
if (doCache) {
event.waitUntil(
caches.open(CACHE_NAME)
.then(function(cache) {
fetch('asset-manifest.json')
.then(response => {
response.json();
})
.then(assets => {
// We will cache initial page and the main.js
// We could also cache assets like CSS and images
const urlsToCache = [
'/',
assets['main.js']
];
cache.addAll(urlsToCache);
})
})
);
}
});

// Here we intercept request and serve up the matching files
self.addEventListener('fetch', function(event) {
if (doCache) {
event.respondWith(
caches.match(event.request).then(function(response) {
return response || fetch(event.request);
})
);
}
});

Now, check if the browsers support service staff and then register worker.js. To do this, you need to add the following script to the file public/index.html (note that shrink-to-fit=no in the viewport meta tag has been deleted).

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#000000">
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<title>React App</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('worker.js').then(function(registration) {
console.log('Worker registration successful', registration.scope);
}, function(err) {
console.log('Worker registration failed', err);
}).catch(function(err) {
console.log(err);
});
});
} else {
console.log('Service Worker is not supported by browser.');
}
</script>
</body>
</html>

You must restart your application and reload the browser after which you will see the Successful registration of member workers on the developer console. Now, recreate the Lighthouse report.

Step 4 - Improve the Progressive Nature of the Application

Your application will display an original blank div until the JavaScript loads and React hooks the original route. You must make sure your application works without downloading JS and displaying a bit of CSS and HTML before React takes effect. Your updated Index.html will look like this:

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#000000">
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<title>React App</title>
<style type="text/css">
body {
margin: 0;
padding: 0;
font-family: sans-serif;
}
.navbar {
background-color: #01C8E5;
text-align: center;
}
.navbar a {
display: inline-block;
padding: 10px;
color: #fff;
text-decoration: none;
}
.page-info {
text-align: center;
font-weight: bold;
}
</style>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root">
<div class="navbar">
<a href="/">Feed</a>
</div>
<p class="page-info">
Loading an awesome app...
</p>
</div>
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('worker.js').then(function(registration) {
console.log('Worker registration successful', registration.scope);
}, function(err) {
console.log('Worker registration failed', err);
}).catch(function(err) {
console.log(err);
});
});
} else {
console.log('Service Worker is not supported by browser.');
}
</script>
</body>
</html>

Now, use Lighthouse to retest your application, and you'll see an improvement in the performance of the application.

Step 5 - Add the Splash Icon

You are required to add a 512x512 icon to display on the screen. To do so, you will have to update the manifest.json file and add the t0 icon to the public directory.

{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "icon-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"start_url": "/",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

Also, use the following meta tags to allow the browser to determine that your application is a PWA.

<!-- Tell the browser it's a PWA -->
<meta name="mobile-web-app-capable" content="yes">
<!-- Tell iOS it's a PWA -->
<meta name="apple-mobile-web-app-capable" content="yes">
Step 6 — Implement PWA

Now, only HTTPS is missing, and the cache can be fixed after you deploy the application. Update the doCache flag with true in the worker.js file. Create a new project in the firebase dashboard and name it "Pwa Application." Then, run the following command in the project directory:

npm install -g firebase-tools
firebase login
firebase init

Your Firebase.json will look like this:

{
"hosting": {
"public": "build",
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
]
}
}

After initializing, building and deploying your application.

  • npm run build
  • firebase deploy

You will see results after you test the application using Lighthouse on the deployed URL.

Final product with Lighthouse

Finally, you have created your first progressive web application with React.js!

Originally published by Manoj Kumar Bardhan at  dzone.com

=======================================================

Thanks for reading :heart: If you liked this post, share it with all of your programming buddies! Follow me on Facebook | Twitter

Learn More

☞ Understanding TypeScript

☞ Typescript Masterclass & FREE E-Book

☞ React - The Complete Guide (incl Hooks, React Router, Redux)

☞ Modern React with Redux [2019 Update]

☞ The Complete React Developer Course (w/ Hooks and Redux)

☞ React JS Web Development - The Essentials Bootcamp

☞ React JS, Angular & Vue JS - Quickstart & Comparison

☞ The Complete React Js & Redux Course - Build Modern Web Apps

☞ React JS and Redux Bootcamp - Master React Web Development