Google+
Shineyrock web design & consultancy

Shineyrock

blog

  • dislike -9 17

    Testing Components in React Using Jest and Enzyme

    This is the second part of the series on Testing Components in React. If you have prior experience with Jest, you can skip ahead and use the GitHub code as a starting point. 

    In the previous article, we covered the basic principles and ideas behind test-driven development. We also set up the environment and the tools required for running tests in React. The toolset included Jest, ReactTestUtils, Enzyme, and react-test-renderer. 

    We then wrote a couple of tests for a demo application using ReactTestUtils and discovered its shortcomings compared to a more robust library like Enzyme.

    In this post, we'll get a deeper understanding of testing components in React by writing more practical and realistic tests. You can head to GitHub and clone my repo before getting started.

    Getting Started With the Enzyme API

    Enzyme.js is an open-source library maintained by Airbnb, and it's a great resource for React developers. It uses the ReactTestUtils API underneath, but unlike ReactTestUtils, Enzyme offers a high-level API and easy-to-understand syntax. Install Enzyme if you haven't already.

    The Enzyme API exports three types of rendering options:

    1. shallow rendering
    2. full DOM rendering
    3. static rendering

    Shallow rendering is used to render a particular component in isolation. The child components won't be rendered, and hence you won't be able to assert their behavior. If you're going to focus on unit tests, you'll love this. You can shallow render a component like this:

    Full DOM rendering generates a virtual DOM of the component with the help of a library called jsdom. You can avail this feature by replacing the shallow() method with mount() in the above example. The obvious benefit is that you can render the child components also. If you want to test the behavior of a component with its children, you should be using this. 

    Static rendering is used to render react components to static HTML. It's implemented using a library called Cheerio, and you can read more about it in the docs

    Revisiting Our Previous Tests

    Here are the tests that we wrote in the last tutorial:

    src/components/__tests__/ProductHeader.test.js

    The first test checks whether the ProducerHeader component has an <h2> tag, and the second one finds whether it has a CSS class named title. The code is hard to read and understand. 

    Here are the tests rewritten using Enzyme.

    src/components/__tests__/ProductHeader.test.js

    First, I created a shallow-rendered DOM of the <ProductHeader/> component using shallow() and stored it in a variable. Then, I used the .find() method to find a node with tag 'h2'. It queries the DOM to see if there's a match. Since there is only one instance of the node, we can safely assume that node.length will be equal to 1.

    The second test is very similar to the first one. The hasClass('title') method returns whether the current node has a className prop with value 'title'. We can verify the truthfulness using toBeTruthy().  

    Run the tests using yarn test, and both the tests should pass. 

    Well done! Now it's time to refactor the code. This is important from a tester's perspective because readable tests are easier to maintain. In the above tests, the first two lines are identical for both the tests. You can refactor them by using a beforeEach() function.  As the name suggests, the beforeEach function gets called once before each spec in a describe block is executed. 

    You can pass an arrow function to beforeEach() like this.

    src/components/__tests__/ProductHeader.test.js

    Writing Unit Tests With Jest and Enzyme

    Let's write a few unit tests for the ProductDetails component. It is a presentational component that displays the details of each individual product. 

    Testing Components in React - ProductDetails component highlighted
    We're going to test the section that's highlighted

    The unit test will try to assert the following assumptions:

    • The component exists and the props are getting passed down.
    • The props like product's name, description, and availability are displayed.
    • An error message is displayed when the props are empty.

    Here is the bare-bones structure of the test. The first beforeEach() stores the product data in a variable, and the second one mounts the component.

    src/components/__tests__/ProductDetails.test.js

    The first test is easy:

    Here we use the props() method which is handy for getting the props of a component.

    For the second test, you can query elements by their class names and then check whether the product's name, description etc. are part of that element's innerText

    The text() method is particularly useful in this case to retrieve the inner text of an element. Try writing an expectation for the product.status() and see if all the tests are passing.

    For the final test, we're going to mount the ProductDetails component without any props. Then we're going to look for a class named '.product-error' and check if it contains the text "Sorry, Product doesn't exist".

    That's it. We've successfully tested the <ProductDetails /> component in isolation. Tests of this type are known as unit tests.

    Testing Callbacks Using Stubs and Spies

    We just learned how to test props. But to truly test a component in isolation, you also need to test the callback functions. In this section, we'll write tests for the ProductList component and create stubs for callback functions along the way. Here are the assumptions that we need to assert.

    Testing components in React - ProductList Component
    1. The number of products listed should be equivalent to the number of objects the component receives as props.
    2. Clicking on <a> should invoke the callback function.

    Let's create a beforeEach() function that fills in mock product data for our tests.

    src/components/__tests__/ProductList.test.js

    Now, let's mount our component in another beforeEach() block.

    The ProductList receives the product data through props. In addition to that, it receives a callback from the parent. Although you could write tests for the parent's callback function, that's not a great idea if your aim is to stick to unit tests. Since the callback function belongs to the parent component, incorporating the parent's logic will make the tests complicated. Instead, we are going to create a stub function.

    What's a Stub? 

    A stub is a dummy function that pretends to be some other function. This allows you to independently test a component without importing either parent or child components. In the example above, we created a stub function called handleProductClick by invoking jest.fn()

    Now we just need to find the all the <a> elements in the DOM and simulate a click on the first <a> node. After being clicked, we'll check if handleProductClick() was invoked. If yes, it's fair to say our logic is working as expected.

    Enzyme lets you easily simulate user actions such as clicks using simulate() method. handlerProductClick.mock.calls.length returns the number of times the mock function was called. We expect it to be equal to 1.

    The other test is relatively easy. You can use the find() method to retrieve all <a> nodes in the DOM. The number of <a> nodes should be equal to the length of the productData array that we created earlier. 

    Testing the Component's State, LifeCycleHook, and Method

    Next up, we're going to test the ProductContainer component. It has a state, a lifecycle hook, and a class method. Here are the assertions that need to be verified:

    1. componentDidMount is called exactly once.
    2. The component's state is populated after the component mounts.
    3. The handleProductClick() method should update the state when a product id is passed in as an argument.

    To check whether componentDidMount was called, we're going to spy on it. Unlike a stub, a spy is used when you need to test an existing function. Once the spy is set, you can write assertions to confirm whether the function was called.

    You can spy on a function as follows:

    src/components/__tests__/ProductContainer.test.js

    The first parameter to jest.spyOn is an object that defines the prototype of the class that we're spying on. The second one is the name of the method that we want to spy. 

    Now render the component and create an assertion to check whether spy was called.

    To check that the component's state is populated after the component mounts, we can use Enzyme's state() method to retrieve everything in the state. 

    The third one is a bit tricky. We need to verify that handleProductClick is working as expected. If you head over to the code, you'll see that the handleProductClick() method takes a product id as input, and then updates this.state.selectedProduct with the details of that product. 

    To test this, we need to invoke the component's method, and you can actually do that by calling component.instance().handleProductClick(). We'll pass in a sample product id. In the example below, we use the id of the first product. Then, we can test whether the state was updated to confirm that the assertion is true. Here's the whole code:

    We've written 10 tests, and if everything goes well, this is what you should see:

    Final output with tests passing

    Summary

    Phew! We've covered almost everything that you need to know to get started with writing tests in React using Jest and Enzyme. Now might be a good time to head over to the Enzyme website to have a deeper look at their API.

    What are your thoughts on writing tests in React? I'd love to hear them in the comments.

    martijn broeders

    founder/ strategic creative at shineyrock web design & consultancy
    e-mail: .(JavaScript must be enabled to view this email address)
    phone: 434 210 0245

By - category

    By - date