11/30/2015

Testing React Components With Jasmine

Making a React component feels straight forward. Testing a React component should feel just as straight forward. Now that I have built tests for my React code, I can say it is. React keeps it's object representations simple. This simplicity works to the developer's advantage when it comes to testing. The trick to testing React components is to make sure you are testing your component, and that you are not testing React. Let's take our simple component from the last two posts and add some tests to it.

My goal in unit testing is always to do blackbox isolated testing. This means that I don't test values and properties inside the function, or object, I'm testing. I restrict my expectations to what is returned from the function. Isolated testing means that I mock out other objects and data endpoints so that any external interactions are controlled. Depending on the design of external code, or the data returned from an endpoint, can lead to brittle tests. There is a place for external interaction testing in integration testing, not unit testing.

We are going to test the sign up email section from the last 2 posts which can be found here and here. React v0.14 dropped in the interim so those articles will need to be updated, but the fully compliant 0.14 code can be found in the example on jsfiddle.

The functionality is pretty simple, so our tests will be too. First, let's write a test to check the component's defaults.

    it("Defaults correctly", function () {
        expect($('#submitEmail p').text()).toEqual(paragraph);
        expect($('#submitEmail button').text()).toEqual('Submit');
        expect($('#submitEmail button').attr('disabled')).not.toBeDefined();
    });
We are using ReactDOM which is browser based, and I prefer simplicity for the example, so I am using jquery to select the relevant DOM elements. This could just as easily be done server side with phantomjs via Karma. React is isomorphic (meaning it can render server side) but I wouldn't test your code that way if your user-facing code isn't isomorphic. Your tests should reflect the user environment as much as possible.

The main feature of our application is the change of content when the Submit button is clicked. Let's write a test to click the button, then check the content:

    it("onClick updates elements", function () {
        $('#submitEmail button').click();
        expect($('#submitEmail p').text()).toEqual(thankYou);
        expect($('#submitEmail button').text()).toEqual('Thank You');
        expect($('#submitEmail button').attr('disabled')).toEqual('disabled');
    });

Now that we have our tests, how do we get the component on the page? Let's add code to render the element before the tests run using the beforeEach clause:

    beforeEach(function () {
        ReactDOM.render(React.createElement(Root, {
            text: paragraph,
            disabled: false,
            label: 'Submit'
        }), document.getElementById('submitEmail'));
    });

We are almost done. Everything will run as is, but we risk polluting all the tests after the first test. Each subsequent test will have artifacts from the test before it. We really need each test to have it's own fresh rendered component. This is easily accomplished by emptying out the render area with the afterEach clause:

    afterEach(function (){
        $('#submitEmail').empty();
    })

That's it. Testing React is as simple as writing it. Remember to focus on testing your component, and not to test React. Happy testing!


Full Example: