Photo by Timothy Dykes on Unsplash

Testing Context-Wrapped React Components

Erin Greenhalgh
Software @ Fast Radius
3 min readApr 23, 2021

--

Summary

In our frontend application, we make use of components that subscribe to context providers, and those providers often contain state-updating logic. Test setup for context-wrapped components is often confusing, and quite frankly, a pain.

There are two main options for testing these context-wrapped components:

  1. Use a static context provider with hard-coded data.
  2. Use your custom context provider, allowing the component under test to trigger any logic contained in the provider.

In this post, I’ll discuss the two approaches and why you might use one over the other.

Basics of React Context

Before we jump into testing, let’s review some key attributes of React Context. (I’ve paraphrased these points from the React docs here and here.)

  • Context allows you to share data that is global to a component tree without having to pass props between every parent and child
  • You can create a context with React.createContext
  • a child component wrapped in a Provider and then a Consumer is subscribed to data changes from the Provider
  • For functional components, you can use the useContext hook to subscribe to data changes from the Provider without explicitly wrapping your child component in a Consumer

Context Patterns

Below is an example of a pattern we use often to create components that wrap the actual Provider component. In this example, MyFormProviderimplements logic that controls the value prop that is passed to React’s Provider component on line 14.

Testing Options

As I said above, we have two options when we want to test a component that subscribes to MyFormContext. For this discussion, what our component specifically does is less important, but for the sake of this example, let’s say it’s a component that takes line items from the context, and allows the user to bulk select them.

Approach 1: Use a static Provider, passing in hard-coded data to value :

Highlights:

  • requires data setup for value so that you can get your DOM into the expected state
  • you won’t have to worry about mocking out any other logic that would be called in a custom provider, like an API call
  • often can be tested synchronously, since the component renders in the state you expect
  • sometimes doesn’t allow you to test the user interaction, which may make the test more succinct but obscure how the DOM can get into this state

Approach 2: Use your custom provider with all its logic

Highlights:

  • requires only initial data setup, then relies on simulated interactions to get the DOM into the expected state
  • more closely mirrors what the component would look like in production
  • allows you to test the way a real user would interact with your component
  • may require more test setup, if you need to mock out logic that is used in the customer provider, like API calls
  • may need to be tested async if user interactions trigger async state updates in the custom provider

When to Use Which Approach

Neither way is “right”, but my recommendation is to prefer testing with the custom context provider for these reasons:

  • It better matches the way the component is rendered in production
  • You have to do less thinking about setting up data in the right state. You just need to think about the initial inputs to the context, and then you can trust that any other values that it works with will be properly updated (assuming that context component itself has been unit tested.)
  • It allows you to test the feature more as a user would, without manipulating the data to get the DOM into a certain state

However, here’s when I would recommend testing with the static provider:

  • if you are testing lower-level children that render data from the provider but don’t update that data
  • if the test setup required to render the custom provider is so onerous that it provides a lot of friction to writing the test. In this case, use the custom provider at the top level component for an integration-style test, and use the static provider for unit-testing the children.

--

--

Erin Greenhalgh
Software @ Fast Radius

Senior software developer with full stack experience and frontend expertise