Evaluating React's Documentation on Testing

·

5 min read

Many people have written about automated testing, and some of the ideas from those writings inform the way I evaluate React's Docs Testing Recipes. In particular, my evaluation is organized around the ideas of implementation, state, and separation of concerns. I will also provide a few recommendations for the examples provided in the Docs.

There is a tendency to avoid testing implementation details, which in the strictest sense means that only surfaces exposed to the outside world would require automated testing. End-to-end tests meet this stringent requirement; however, such tests can cover only a narrow set of paths, and attempts to cover more than a handful of happy paths inflates the testing suite into a slow process. Many organizations run slow tests less frequently than they run the faster tests, which are usually the unit tests.

The testing pyramid is a tool that provides value in different ways throughout the software development process. When developing on a local machine, one way unit tests is useful is for detecting breaking changes to the interfaces of existing functions. Unit tests do not replace end-to-end tests, but an unintentionally failing unit test is an indicator that the changes to the code yield an incorrect output or worse.

Unit tests reveal some level of implementation details because the tests rely on the functions working as they're currently expected to. Some of the tests in the React Docs assert on the state of individual components while others look at interaction instead of state.

image.png

Testing interaction is another way of revealing implementation details, but it's inherently different from testing on state. Calling the function onChange is not a part of the Toggle interface; consequently, non-breaking refactors may fail this test. The developer now must decide what to do with their changes to the code and this test. Instead of mocking the function onChange in a unit test, the developer can assert the outcome of the changes made by calling the onChange function in an integration test.

In addition to mocking functions, the React Docs also recommend mocking requests under the section "Data Fetching". In the absence of libraries such as Redux, the logic for calling APIs sometimes lives inside of components. This convention can be found across official React documentation. An alternative to mocking the API call would be to relocate the call and pass in the data from the call to the component. Compartmentalizing business logic from network calls is a way to separate concerns, which offers benefits. If this network call occurs across the code in a number of places, the implementation can be consolidated, and future changes are made in one place.

Developers can benefit from removing the data fetching logic out of components without introducing a new library. React comes with Context.Provider , wherein API calls can be made. The introduction of a new pattern in an existing code base should be done carefully and with the consent of collaborators of the code base.

Each of the testing recipes is a tool within the developer's toolkit for establishing code coverage. In combination with ideas borrowed from other parts of software development, developers can code, refactor, and ship with confidence.

Further reading: