In the latest three posts we started building a React app to play the Darts game of tactics. Initially, the static version of the app was created, building on the best practices as described by Pete Hunt in the post on the official React site. In the second part, we implemented interaction in the app and we started using state. At the end of that post the result was a working game. In the final part so far we investigated the performance of the app and found out that immutability is the key for a fast app with minimal needless rerenders.
But until today no word was written on testing all this stuff. So I thought it was time to dive into this thing called unit tests.
What do we test?
If you are like me, you would probably think that the first thing to test are the functions (callbacks) that are executed when different buttons in the app are clicked. Some how we need to make sure that clicking a number marks it as hit, clicking the plus button should add points for the active player and so on.
Start with testing your components
After reading some posts on unit testing React, I found out that testing the components render function is the first step. Of course, for a small app you can just see if the components are rendered well, but what about scalability? In this case we are planning to build a dart app with multiple games and probably a lot of components. If we setup the tests for the components now, future changes in the components won’t break our code. These changes may break the tests, but then it is up to us to implement the changes in such a way that the tests all pass.
How do we unit test components?
Like everything else in React, there are various modules from npm to setup our test environment. In this post I will explain one combination of modules to test the components from the command line. The least we need today are the following modules:
- tape/tape-watch (the test runner)
- enzyme or react-addons-test-utils (utilities to build up the tests)
- jsdom (to setup a virtual dom for testing callbacks)
- faucet (for proper styling of the test output)
As before, the complete code for this post is available on Bitbucket.
We place our tests in their own directory /test. Subsequently, we create one test file per component. As our React components are build up as a tree, the easiest way to start is to take a leaf component that does not depend on other components. If we create some tests for such a component and they all pass, we continue with a component a little closer to the root. This way we proceed until we reach the root component.
Writing our first unit test
First look at the code below which will test the PlayerTurner component. Remember that this component is nothing more than a button with an icon inside which changes the active player when clicked.
In the code, we import some modules and write one function that is exported. The function test imported from tape is used to write the actual test. One typically nests these test functions one or more layers deep to group the tests. Here you see that we have two tests within the outer test call.
The test function is passed a string and a callback, the latter having one argument, a test instance. In the first test, we like to make sure that the PlayerTurner component contains an <img> tag with the correct src tag set. So we tell the test runner that we plan to make exactly one assertion for this test (t.plan(1)) and we use the pretty nice enzyme api (which looks a lot like jQuery but works on the virtual DOM) to find the src of the <img> tag and we check that the ‘one-finger-tap’ string is contained in the src attribute.
But wait a minute! What is this result variable that you created but never explained? Yes, I forgot. Enzyme gives us two ways to render a component, shallow and mount. The names are pretty clear, shallow will only create the component itself, no children and mount also mounts the component to a (virtual) dom. We come to that in the next test.
So in fact all we do in the first test is shallow rendering the component and checking that it contains an img tag with a specific src attribute.
Testing the onClick callback
In the second test we would like to make sure that when we click the PlayerTurner, it calls the changeTurn callback. Therefore,we need to be able to simulate a click and hence, we need to mount the component. In unit tests we don’t have a real dom, but using the jsdom module we can setup a virtual one to play with. The setup is done in ./setup.js which is quite self explanatory. After mounting the component, we find the button with the onClick handler attached and simulate a click on it. The test works as follows: By telling tape that we do exactly one assertion (t.plan(1)), the test will only pass if the assertion has been executed. Putting the assertion in the callback the test will only succeed when the callback is actually called.
Testing other components
If you look at the code of the other tests you’ll see that mainly t.ok and t.equal are used. The tricky component to test is the <Finished /> component. We have to do some mocking before we can test this component, because it requires a nearly finished game state. To make this work I created some helper functions.
We have setup an environment to unit test React components. Although it is possible to use the test utils from React itself, the enzyme module from AirBnB works like a charm and makes writing tests much more fun. Note that you can simply exchange the tape testing library for another one, for example a combination of mocha with expect. More on this in a future post!