When to Unit Test your React App

Image by Bernd Hildebrandt from Pixabay

React testing is trending toward functional testing: test your app the way a user would use it. The tests go through user flows, and are decoupled from the code (testing behavior over implementation). This is fantastic when it comes time to refactor your code, or update the code without changing the behavior. Since the tests are tied only to the behavior, there’s no need to update the tests. Champagne all around! 🍾

This leaves a question, though: is there any space left in React for unit testing (tests that isolate one unit of code, often a function or component)?

Unit tests can be functional tests

For example, say you have a component, Quote, that displays a pithy quote and links to the author’s website. Some authors might not have a web site, though (Abraham Lincoln was notoriously bad at keeping up with web technology). Your tests need to check that the quote renders correctly for both situations: authors who have a link, and those who don’t.

One quote with link to author, another without a link to author
One quote with link to author, another without a link to author
When will Abe get a web presence?

These are functional tests for a user experience: they confirm the author’s name is either a link to the right destination, or plain text without a link for author data without a web site link. But they’re also unit tests for the Quote component. The test can render the Quote component with the quote and author data as props, and the tests are isolated to that one component. So here you’re writing unit tests simply because all the code for your functional test is contained within one unit.

Okay, so this example seems to be “unit testing as a technicality.” You’re actually doing functional tests that just happen to be unit tests. Are there any reasons you might want to unit test deliberately? Read on, my friends.

Unit tests help diagnose other failing tests

In a large, complicated project, there could be dozens or hundreds of lines of code responsible for a certain functional test, and narrowing down the cause of a failure could take a fair amount of time and effort. In some cases, you could potentially spend more time tracking down test failures than you would maintaining unit tests.

If there is a likely potential point of failure for one or more functional tests, you might consider making a unit test to cover it. This way, you can easily zero in on what is causing the functional test to fail — or eliminate a potential reason the functional test is failing.

As a simple example, imagine an app where someone can look up laws about backyard chicken coops for a particular city. The user selects a state from a drop-down menu, which populates a second drop-down menu with cities in that state that have such laws. Some states (I’m looking at you, California) have a lot of cities, so the city menu is broken into sub-menus by alphabet if there are more than 20 cities in the state.

Dropdown for state, with dropdown for cities with alphabetical sub-menus
Dropdown for state, with dropdown for cities with alphabetical sub-menus
oof, Califorina

You may write a functional test where a user navigates to the search page, selects a state and a city, and sees the correct legal information. The most complicated part of this test — and most likely point of failure — is the appearance and content of the second drop-down containing the city options.

So here you might want to write unit tests to support your functional test. These tests could render only the form element and ensure that the second menu is not there when you load the page, that it contains the correct data for a state with 20 or fewer cities that have ordinances, and that the menu paginates properly for cities that have more than 20 cities.

This type of unit test makes it easier to diagnose failing functional tests. If all of these unit tests pass and your functional test fails, you know that the problem lies elsewhere. But if you find a failing unit test, you know what you have to fix in order to get the functional test to pass.

Unit tests confirm data sent to the server

For example, say you have a Redux action creator that processes form data and creates a JSON patch to update an entity on the server. You may want to write unit tests for this action creator to make sure that the JSON patch includes all the necessary data (and none of the extraneous data) for particular sets of form inputs.

This is testing implementation to be sure! The user neither knows nor cares about the exact data being passed to the server. However, functional tests usually don’t involve the actual server; that’s reserved for end-to-end tests. So this type of unit test will highlight where an end-to-end test is (or isn’t!) breaking down — or even better, prevent you from pushing code that breaks end-to-end tests in the first place.

Unit tests for edge cases that don’t affect render

input to guess a secret word, and table of previous guesses with count of letters that match the secret word for each guess
input to guess a secret word, and table of previous guesses with count of letters that match the secret word for each guess
spoiler alert: the secret word is “party”

Imagine you have a word game app that needs to check how many letters a guess has in common with the “secret word.” You write a helper function for this, which can easily be unit-tested to make sure it does the right thing when (a) the guess has more than one of the same letter, (b) the secret word has more than one of the same letter, (c ) the word has all the same letters as the secret word, (d) the words have zero letters in common.

The app does the same thing with the match count regardless of the above conditions, so it’s not necessary to render your JSX to test edge cases for this particular function. You will want to test this logic to make sure any added code doesn’t introduce errors, though. So here, unit tests of this function are the best way to go.

Summary

  • my functional tests happen to test only one unit
  • there’s a likely point of failure in a longer functional test
  • I want to confirm that the correct data is sent to the server
  • I’m testing edge cases for a helper function that don’t affect rendering

Do you agree or disagree with me? When do you use unit tests? Let me know in the comments!

Teacher, coder and testing enthusiast. Visit me at https://bonnie.dev

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store