Previously we tested React components with Jasmine and jQuery, but what if we want to test without having a DOM available? React's testing tools have you covered with Shallow Rendering. Shallow Rendering will render the object as it would before sending it to be painted on the screen. This makes the final state of a component available for testing. Testing with React can now be done even easier on any platform: web, command-line, node. The best part is that this form of testing supports the black box unit testing approach. You are testing only one component independent of its dependencies.
Shallow Rendering is still a work in progress, but one with a lot of promise. There are a few things you need to know before giving it a try:
- You need to use a version of React with addons, specifically the TestUtils.
- v0.14.x has a bug that won't work with setState in ComponentWillMount. This is resolved in the upcoming version. The fiddle linked below is using the latest dev build over http.
- The TestingUtils function Simulate will not work. Events need to be called from the DOM event, not the handler
- Find a good library to search JS objects. I use underscore.
With that out of the way, let's look at the awesomeness.
The first piece is to create a renderer. The renderer will maintain state and current rendering for our component.
var shallow = React.addons.TestUtils.createRenderer();
The next piece is to create an instance of a React element. That instance is passed into the renderer. Finally, you request the rendered output via getRenderedOutput(). Next, we run our tests.
var elm = React.createElement(Subscribe, {
text: paragraph,
disabled: false,
label: 'Submit'
});
shallow.render(elm);
out = shallow.getRenderOutput();
The above tests use underscore's findWhere function to locate the particular node in the rendered object that I am looking for. Once we have the node, we can evaluate it it's state and contents.
it("Defaults correctly", function() {
var paragraph = _.findWhere(out.props.children, {type:'p'}).props.children;
expect(paragraph).toEqual(paragraph);
var button = _.findWhere(out.props.children, {type:'button'}).props;
expect(button.disabled).toBeFalsy();
});
Now you can use the shallow render to test the initial state of a component, but what if have functionality to test? Then we need to force events to occur, and find the new rendered output. This particular puzzle was solved by Marcin Grzywaczewski in the article http://reactkungfu.com/2015/07/approaches-to-testing-react-components-an-overview/. It turns out we need to invoke the DOM version of a function. The React component will pick it up and run the bound function. Once run, we need to re-render to get the new output.
In the above example, we find the necessary button in the rendered component. We invoke the button's regular onClick. The bound function is called "_onClick" and can't be called directly. The onClick will run it just as if this was a real DOM element. Finally, we ask the renderer for the new output after the function has run.
var button = _.findWhere(out.props.children, {type:'button'});
button.props.onClick();
out = shallow.getRenderOutput();
Here we test the same way but now we test against the revised output which has the changes we would expect.
var button = _.findWhere(out.props.children, {type:'button'});
expect(button.props.disabled).toBeTruthy();
var paragraph = _.findWhere(out.props.children, {type:'p'}).props.children;
expect(paragraph).toEqual(thankYou);
The Shallow Render is my new favorite thing about React. It only emphasizes how well React manages a strict seperation of concerns, and how useful that can be. Keep your eyes open for more as the Shallow Rendering is improved over time. Happy testing!
The full JSFiddle is here --> http://jsfiddle.net/dposin/t3zrpgkx/
(I can't embed it because it needs to run over http to pick up the latest build.)