Testing your TypeScript React components with Jest

Taking a BDD (Behavior Driven Development) approach to your JavaScript application can help you save countless hours by improving code quality and reducing maintenance requirements to minimize the project’s risk. It helps ensure that our software meets user need and that the underlying requirements are easily understood. Testing our React components might seem like a daunting process but frameworks like Jest and Enzyme make it easy.

To get started with Enzyme and Jest to test your TypeScript React application run the following commands in the root of your project.

 npm install enzyme ts-jest react-test-render --save-dev

Make sure you also install the associated type files through your typings manager or the latest method of installing @types/packageName.

Inside our package.json we will setup our configuration letting Jest know which preprocessor to use, where to find our tests, and what kind of files we are going to test. Add the following to your package.json:

"jest": {
    "transform": {
        ".(ts|tsx)": "<rootDir>/node_modules/ts-jest/preprocessor.js"
    },
    "testRegex": "(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$",
    "moduleFileExtensions": [
        "ts",
        "tsx",
        "js"
    ]
}

This basically says we are going to use the preprocessor we installed with the ts-jest package and to look inside of a folder named __tests__ for TS, TSX, or JS files to test that are named *.test.ts or *.spec.ts.

So let’s create a folder in the root of our project called __tests__ and add a test called Textbox.spec.tsx. This will be our test for the following component:

import * as React from "react";

export default class Textbox extends React.Component<ITextboxProps, ITextboxState> {
    constructor() {
        super();
        this.state = {
            className: "",
            inputVal: ""
        };
    }
    onFocus = () => {
        this.setState({
            className: "is-focused"
        });
    }
    onBlur = () => {
        this.setState({
           className: ""
        });
    }
    onChange = (e: React.FormEvent<HTMLInputElement>) => {
        this.setState({
            inputVal:  (e.target as HTMLInputElement).value
        });
    }
    render() {
        return (
<div>
                <input type="text"                      className={this.state.className}                     onFocus={this.onFocus}                     onBlur={this.onBlur}                     onChange={this.onChange}                     value={this.state.inputVal}/></div>
);
    }
}

interface ITextboxProps {
}
interface ITextboxState {
    className: string;
    inputVal: string;
}

This is just a simple example component that has a few event handlers that we will test.

For our tests we will take a BDD structure in describing our scenario in a way that is easily understood by anyone. To do so we will make use of `describe` and `it` from the Jest library.

To start we will need to import React, our component we are testing, a renderer from react-test-renderer, and shallow/mount from enzyme. Shallow and mount allow us to test our react component state.

import * as React from "react";
import Textbox from "../src/Components/Textbox";
import renderer from "react-test-renderer";
import {shallow, mount} from "enzyme";

Now we can setup our scenario and first test. This first test will ensure that the rendered Component matches the snapshot from the virtual dom ensuring that no unintended outside forces are impacting our component:

describe("Textbox", () => {
    it("renders as expected", () => {
        const component = renderer.create(<Textbox/>).toJSON();
        expect(component).toMatchSnapshot();
    });
});

Next we will create a test that ensures that when we focus the input inside our component that the expected class is applied:

describe("Textbox", () => {

... previous tests ...

    it("adds is-focused class on focus", () => {
        const component = mount(
            <Textbox />
        );
        const input = component.find("input");
        input.simulate("focus");
        expect(input.hasClass("is-focused")).toBe(true);
        input.simulate("blur");
        expect(input.hasClass("is-focused")).toBe(false);
    });
});

In this example we use mount to render a react component into the document. We use the component find method to locate the inner `input` and simulate a focused event. We then use the component hasClass test method to check if the “is-focused” class is applied.

Next we will use a similar set of methods to test if the value changes when we fire the onChange event:

describe("Textbox", () => {
    it("changes value when state changed", () => {
        const component = mount(
            <Textbox />
        );
        const input = component.find("input");
        const newText = "updated text";
        input.simulate("change", {target: {value: newText}});
        expect(input.props().value).toBe(newText);
    });
});

Similar to the previous example we mount the Textbox and locate the inner input. We then simulate the onChange event passing a new value. We then check the input’s updated value to ensure that it is set to the expected value of newText.

For more examples you can check the Jest docs on testing React components.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s