
With the introduction of React version 16 in 2017, hooks now make it possible to reuse stateful logic between components. However, with these new features, engineers must revisit previously defined conventions for using React. Unit testing is one of these practices.
The Web Tools Platform team’s mission is to create performant web visualizations that enable teams at Uber’s Advanced Technologies Group (ATG) to build better tools for self-driving vehicles and, in part, produce open source software.
As a summer engineering intern on Uber ATG’s Web Tools Platform team, I explored and defined new best practices to test our various React components after my team heavily altered some of the logic in our application to visualize self-driving vehicle data.
While implementing functional components with hooks, our team noticed a lack of documentation on React version 16 unit testing available online. In the process, we determined various best practices for unit testing functional components with hooks. We hope other engineering teams will find these best practices useful and can also apply these approaches to their unit testing.
Basic setup for testing components
When I began as an intern, the Web Tools Platform team had just finished adding to and refactoring various components in an application to visualize autonomous vehicle data. We needed to test those alterations and new pieces of logic to ensure particular components still worked as expected. During this evaluation, we compiled best practices and procedures for testing React version 16 components.
To start, we used Enzyme to create shallow renderings of components that we could then compare to snapshots.
Creating these renderings is a two-step process:
- Define a wrapper around the shallow rendering of a component. Include props if they are required.
- Create a basic snapshot test to ensure that the JSX returned is correct and consistent with what is expected. To perform a snapshot test, render a component, take a snapshot of the JSX output, and then compare it to a reference snapshot file stored in the test. The snapshot test fails if the two snapshots do not match.
Although snapshots provide an overview of what a component renders, it is still necessary to test the components’ internal logic.
Testing the logic of class components
Testing the logic of class components in React version 16 is fairly straightforward. Developers can test class component logic to access state variables and props in order to reference the variables of a particular class context.
As such, the first step to testing the logic of a class component is to retrieve the wrapper’s underlying class instance.
Testing internal functions
In general, we tested the logic of internal functions in a class component by ensuring that React altered values as expected after calling them. Our team tested specific functions that altered a state variable given that certain conditions held. We evaluated whether or not the value of the variable changed after the function was called.
Testing whether componentDidMount executed correctly
In a class component, any other function besides render is optional. However, in the above example, the component includes a componentDidMount method, which sets up an interval to periodically update the value of a state field until the component is unmounted. To test this functionality, we checked if componentDidMount correctly set the state variable to a function.
Note that we did not directly test what the value returned by the function is or whether it is correct. This test only ensures that the logic of the immediate function, componentDidMount is correct; to evaluate the logic of the function, one would need to run a separate test to do so. 
Testing whether componentWillUnmount executed correctly
If it is defined, componentWillUnmount is called immediately before a component is unmounted from the DOM (Document Object Model) element it was originally mounted to. Often, componentWillUnmount clears the component’s interval so that the setState of the state variable does not update after each interval as part of cleanup. In our testing, we checked to make sure that after componentWillUnmount is called, the wrapper has an undefined intervalId, which demonstrates that it has been cleared. If the wrapper’s intervalId is present, componentWillUnmount may not be working properly.
Functional components
Unlike class components, we cannot test functional components using instances. Since a functional component is inherently just a function, there is no way to instantiate one and then directly call its variables or functions.
To overcome this issue, our best practice is to separate more complex segments of logic within the functional component and rename them as their own methods in other files outside of the immediate component.
Testing internal functions
The example, above, leverages the functional component version of onOpen.
To address this, we moved the bulk of logic from onOpen to the Utils file and is called by exporting the newly made function and calling with the state variable selectOpen and its set method. In this way, it becomes possible to test the logic of onOpen.
When we test functional components such as onOpen, we have to check to see if localSetSelectOpen was called. When evaluating class components, however, we can determine the value of the state variable immediately after modifying it.




