Notes of Maks Nemisj

Experiments with JavaScript

How to test React.js components

In this article I will explain “Why should you test react.js components”, “How can you do testing” and “What are the problems you might come across”. Testing solution which I use, doesn’t rely on the Test-utils of React and on DOM implementation ( so that you can do the node.js testing ).

Why/How to test React components?

The main question you might have with react components – “Why to test react components and what exactly to test?”

Setup

Before I will continue with the explanation I would like to show you a sample setup we will use. I will have two components ParentComponent.jsx and ChildComponent.jsx. In render() of ParentComponent.jsx ChildComponent will be rendered based on a ‘standalone’ property.

Definition of ParentComponent.jsx:

var React = require('react');

var Child = require('./ChildComponent.jsx');

module.exports = React.createClass({
  displayName: 'ParentComponent',

  renderChildren: function() {

    if (this.props.standalone === true) {
      return null;
    } else {
      return <Child />;
    }

  },

  render: function() {
    return (
      <div className="parent-component">
        {this.renderChildren()}
      </div>
    );
  }
});

ParentComponent.jsx

Here is definition of the ChildComponent.jsx:

var React = require('react');

module.exports = React.createClass({
  displayName: 'ChildComponent',

  render: function() {
    return <div className="child-component"></div>;
  }
});

ChildComponent.jsx

Code can be found at https://github.com/nemisj/react-mock-testing with the history following this article.

Unit testing

In my opinion whenever you have some logic in the code and it depends on any circumstances it’s important to test it, in order to make sure that what you expect is always valid. While this is obvious for ‘usual’ code, it’s not always clear what to test in react components.

If you look at the example above you will see that render() method of the ParentComponent.jsx will return the following html:

<div class="parent-component">
  <div class="child-component"></div>
</div>

And renderChildren() will return in its turn:

  <div class="child-component"></div>

When seeing this, you might start asking.

Do I have to test HTML…?

That’s a valid question and I was also asking it myself. The answer is – YES you DO have to test it and NO you DON’T have to test it.

Yes, you DO have to test HTML

The reason for that is simple. Think about the HTML asif it’s a return type. Imagine if renderChildren() would return you a real instance of ChildComponent. You wouldn’t even ask whether to test it or not. Because it’s an instance you would just do some kind of instanceof and that’s it. But because in react a markup (HTML) is returned, it feels like a different story. Nevertheless HTML is the only medium there is, so we have to deal with it as it is.

No, you DON’T have to test HTML

When testing component we are not interested in the content of HTML itself. It does not matter what kind of node component returns, either it’s a <span> with a CSS-class or a <div> with an attribute. What is important, is to test what certain element means to us inside our application/code.

Take for example markup of ChildComponent<div class="child-component"></div>.

Whenever it is returned by ParentComponent this markup means to us that there is a ChildComponent instance which is returned. Not a <div> element with css-class “child-component”, but instance of ChildComponent. That’s the reason why we DON’T test HTML as a browser language, but we DO test HTML as an instance definition.

Implicit instance checking

In it’s simplest form in order to test our logic of ParentComponent, we have to test that its render() method returns HTML which will contain <div class="child-component"></div>. Suchwise we can identify that actually ChildComponent was instantiated inside ParentComponent and not something else.

It feels like an implicit instance checking, since we deal not directly with instance, but with the ‘representations’ of it.

Representation Type
<div class=”child-component”></div> ChildComponent.jsx
<div class=”parent-component”></div> ParentComponent.jsx

Writing tests

Let’s look at the possible tests for ParentComponent. I’ve used (React.renderToStaticMarkup)[https://facebook.github.io/react/docs/top-level-api.html#react.rendertostaticmarkup] to do assertions based on string. This method will return string value of rendered component.

In addition such approach allows to run tests in node.js environment without need for having any DOM implementation available inside environment.

Below is the test test/ParentComponent.test.js written using (mocha)[http://mochajs.org/] testing framework.

var React = require('react');
var expect = require('chai').expect;

var ParentComponent = require('../ParentComponent.jsx');

describe('ParentComponent', function() {

  var childType = '<div class="child-component"></div>';

  it('should render with child', function() {
    var markup = React.renderToStaticMarkup(<ParentComponent />);

    expect(markup).to.contains(childType);

  });

  it('should render without child', function() {
    var markup = React.renderToStaticMarkup(<ParentComponent standalone={true} />);

    expect(markup).to.not.contains(childType);
  });

});

test/ParentComponent.test.js

As you can see, there is one test ‘should render with child’ for testing the existence of the ChildComponent inside the html and another test ‘should render without child’ for testing that child component is not returned.

While this solution is working, it has one big disadvantage.

To see this, let’s imagine that definition of the ChildComponent.jsx will change to the following form:

var React = require('react');

module.exports = React.createClass({
  displayName: 'ChildComponent',

  render: function() {
    return (
      <div className="child-component">
        Inner Text
      </div>
    );
  }
});

ChildComponent.jsx

Because the content of the ChildComponent.jsx is changed to <div class="child-component">Inner text</div>, our test will fail.

AssertionError: expected '<div class="parent-component"><div class="child-component">Inner Text</div></div>' to include '<div class="child-component"></div>'

This is the reason why testing HTML feels so wrong at the beginning, because the implementation of the component has dependency on the test of the ParentComponent and deeper nesting will mean bigger change in returned HTML. But, bear with me a little bit more.

Mock

As I told, we are not interested in the HTML itself, but only in the fact that this HTML represents a certain type. If we will mock the ChildComponent with our own definition, then we could abstract the implementation of the child away from the parent.

To do mocking I’ve used rewire library, but you can use the one which better fits your architecture and needs. It’s also possible that you use a Dependency Injection library in your architecture and need another way of mocking.

Rewire library allows patching of the private variables in the module. Just require a module using rewire method and then use __set__ on the module. Let’s look at the example:

var rewire = require('rewire');
var ParentComponent = rewire('./ParentComponent.jsx');

var ChildComponentMock = {};

ParentComponent.__set__('Child', {})

rewire-example.js

In this example Child variable is replaced with an empty object.

This leads my story to the next point.

We can create a mock component and replace the real one. By doing so mock representation will be used, whenever ParentComponent will render. To do comparison we can render this mock separatly and use in assertion.

Below is an implementation of test case together with mock:

var React = require('react');
var expect = require('chai').expect;
var rewire = require('rewire');

var ParentComponent = rewire('../ParentComponent.jsx');

var ChildMock = React.createClass({
  render: function () {
    return <div className="child-mock-component" />;
  }
});

ParentComponent.__set__('Child', ChildMock);

describe('ParentComponent', function() {

  var childType = React.renderToStaticMarkup(<ChildMock />);

  it('should render with child', function() {
    var markup = React.renderToStaticMarkup(<ParentComponent />);

    expect(markup).to.contains(childType);

  });

  it('should render without child', function() {
    var markup = React.renderToStaticMarkup(<ParentComponent standalone={true} />);

    expect(markup).to.not.contains(childType);
  });

});

test/ParentComponent.test.js

Let’s walk through this code.

  • First of all rewire module is required.
  • After that ChildMock component is created. This component will represent our ChildComponent type.
  • Using __set__ method of the rewire, replace the real component with the mock
  • Compare whether the ParentComponent contains ChildComponent mock representation.

As you can see using a mock for ChildComponent we can test whether the ParentComponent uses the correct component.

Small optimization

We can abstract the creation of the mock into a separate function, and make component distinguishable based on the custom tag and not css-class. Using React.createElement we can make custom tags.

function getMock(componentName) {
  return React.createClass({
    render: function () {
      return React.createElement(componentName);
    }
  });
}

var ChildMock = getMock('ChildComponent');

test/ParentComponent.test.js

ChildMock represenation will look like <ChildComponent></ChildComponent>

Testing this.props

Components can be parameterized via the props definition. Now that it’s possible to express type of the component via its HTML representation, let’s think how can we test component. Since it’s an input for our component, it’s vital to test it as well. Imagine that ChildComponent will use property “childName” to render it inside node. If ParentCompoentn passes wrong value to it, we will have incorrect screen.

I have simplified ParentComponent.jsx, removed if statement and added childName property when rendering ChildComponent in the code below:

var React = require('react');

var Child = require('./ChildComponent.jsx');

module.exports = React.createClass({
  displayName: 'ParentComponent',

  renderChildren: function() {
    return <Child childName="Name of the child"/>;
  },

  render: function() {
    return (
      <div className="parent-component">
        {this.renderChildren()}
      </div>
    );
  }
});

ParentComponent.jsx

If we use current implementation of the mock, we will never find out what properties have been passed to the ChildComponent, because they will be dropped in the representation. By slightly modifying our mock component, we could serialize the properties into the HTML and make them comparable. React.createElement can help us with this, because second argument of React.createElement will be converted into attributes of the node. In this way we could pass received properties to it.

function getMock(componentName) {
  return React.createClass({
    render: function() {
      return React.createElement(componentName, this.props);
    }
  });
}

var ChildMock = getMock('ChildComponent');

The only problem with this solution, is that React will skip attributes which don’t belong to HTML, unless they are prefixed with “data-“. This means that we have to iterate over all the properties and append “data-” prefix to all custom attributes. I say to all custom attributes, because we don’t want to prefix attributes which are native to react, like className, disable, etc. We can use DOMProperty.isStandardName object from “react/lib/DOMProperty.js” to find our which properties are native. Also names, must be lowercased, otherwise React will gives us an error that attributes must be all lowercase.

var DOMProperty = require('react/lib/DOMProperty.js');

var createAttributes = function (props) {
  var attrs = {};

  Object.keys(props).forEach(function (key) {
    var attrName = DOMProperty.isStandardName[key] ? key : ('data-' + key.toLowerCase());
    attrs[attrName] = props[key];
  });

  return attrs
}

function getMock(componentName) {
  return React.createClass({
    render: function() {
      var attrs = createAttributes(this.props);
      return React.createElement(componentName, attrs);
    }
  });
}

test/ParentComponent.js

Now, if we instantiate ChildMock with attribute childName, it will additionally have the childName property serialized into the HTML, like this:

<ChildComponent data-childname="Name of the child"></ChildComponent>

In this way we can check both and the type os the returned component and properties which are passed to it.

Shallow rendering

Instead of writing own mockShallow rendering feature of the Test-Utils of the react could be used.

It allows to do the type checking of the return markup like we do. Shallow rendering will return the first level of the components. Unfortunately the problem relyies in the fact, that it gives back the first elements of the given element and in this approach there is a problem.

Nowdays, when React is going away from the mixins because of ES6 classes, people found out another way of getting mixing into play. What they do is wrapping components with virtual components. Virtual components are components which are not visible and have no own representation, but they bring some mixable functionality in wrapped component. For example fluxible is one of this libraries doing that.

To make it clear, look at the following code:

var ParentComponent = React.createClass({
  render: function() {
    return <div/>;
  }
});

function handleMixin(Component) {
  return React.createClass({
    render: function() {
      return React.createElement(Component, objectAssign({}, this.props, this.state));
    }
  });
}

ParentComponent = handleMixin(ParentComponent);

Because components are wrapped around with another components, the first elements for shallow renderer will become not the nested child, but the parent componnent itself. And this is really a problem, since we would like to test the children of this parent, and they’re not available there.

That’s it for today. You can find source code of this article at https://github.com/nemisj/react-mock-testing.

, , , ,

2 thoughts on “How to test React.js components

  • Renan Valentin says:

    Great article Nemisj!

    I’m going to follow your instructions to create my tests. I was using karma with browserify, but I couldn’t make it work very well with code coverage.

    Thank you very much

  • Maks Nemisj says:

    @Renan, tell me about your results, i’m curious whether this helped

Leave a Reply

Your email address will not be published. Required fields are marked *