Inside the Labyrinth · the Koan Blog

Easier React Component Testing through Dependency Injection HOCs

When unit-testing React components, we generally want as little preamble as possible.

At [Koan](https://koan.co/), we have a large React [SPA](https://en.wikipedia.org/wiki/Single-page_application) that makes heavy use of [Redux selectors](https://redux.js.org/recipes/computing-derived-data). These make it easy to get data from our Redux store in the exact derived format we need, but they require a populated store in order to function. This is fine for production, but when writing component unit tests, we generally don't want so much preamble just to test our component's functionality.

To address this, we're using a pattern in our code that's reminiscent of [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection) frameworks in languages like Java, with promising results.

### The Problem

Many of our components look something like this:

```
import { connect } from 'react-redux';
import fooSelector from 'modules/selectors/foo';

const FooComponent = ({ foo }) => (
 // JSX here
);

export const mapStateToProps = (nextReduxState, nextOwnProps) => {
 ...
 return {
   foo: fooSelector.getFoo(nextReduxState),
   // other store-based props here...
 };
};

// A mostly straightforward React component connected to a Redux store.
export default connect(mapStateToProps)(FooComponent);
```

A mostly straightforward React component connected to a Redux store.

Here, we've gone through the effort of making our component a stateless function, so we know that part will be easy to test in isolation. But what about testing `mapStateToProps`? `FooComponent` may be dead simple, but now we've just offloaded the complexity to another function. And while it looks simple enough, there's a problem: that call to `fooSelector`.

Given only the inputs (`nextReduxState` and `nextOwnProps`), we can't deterministically say what the output of the function will be, meaning it's not a [pure function](https://en.wikipedia.org/wiki/Pure_function).

So how do we address this? One way might be to use a [prewritten mock](https://facebook.github.io/jest/docs/en/manual-mocks.html) for the selector module. This could work, but it doesn't actually make our function pure. In addition to that, it can be a bit opaque. So how do we make use of this selector while still having a pure function that's clear to unit test?

### The Dependency Injection Higher-Order Component

The solution we ended up with makes use of everyone's favorite React abstraction, the Higher-Order Component ([HOC](https://reactjs.org/docs/higher-order-components.html)), and its best friend, the HOC utility library [Recompose](https://github.com/acdlite/recompose/). We'll first create an HOC that just simply takes in a component and returns that same component, but with a `fooSelector` prop:

```
import { withProps } from 'recompose';

import fooSelector from 'modules/selectors/foo';

// A higher-order component for injecting `fooSelector` as a prop into a component.
export default Component => withProps({ fooSelector })(Component);
```

A higher-order component for injecting `fooSelector` as a prop into a component.

Here, we're using `[withProps](https://github.com/acdlite/recompose/blob/master/docs/API.md#withprops)` to inject the `fooSelector` module as a prop to the component, while keeping the rest of its props intact.

Now we'll rewrite our original component with this new HOC:

```

import { connect } from 'react-redux';
import { compose } from 'recompose';

import withFooSelector from 'modules/decorators/withFooSelector';

// Our component, but with `fooSelector` passed in from above.
const FooComponent = ({ foo }) => (
 // Still just JSX here
);

export const mapStateToProps = (nextReduxState, nextOwnProps) => {
 ...
 return {
   foo: nextOwnProps.fooSelector.getFoo(nextReduxState),
   // other store-based props here...
 };
};

export default compose(
 withFooSelector,
 connect(mapStateToProps),
)(FooComponent);
```


Here, we've used `[compose](https://github.com/acdlite/recompose/blob/master/docs/API.md#compose)` to chain our HOCs in such a way that `mapStateToProps` gets to make use of `fooSelector` without needing to touch anything other than its own arguments! This is an idea borrowed from dependency injection (DI) frameworks in languages like Java. Now, our unit test for it might look something like:

```
const result = mapStateToProps(someState, {
 fooSelector: {
   getFoo: jest.fn(/* our mocked out `getFoo` function goes here */),
 },
);

// Test the result against our expectations...
```

### Why not use a render prop?

When thinking about this, we considered whether an HOC was really the right approach. After all, [render props](https://reactjs.org/docs/render-props.html) seem to gaining steam in the React community, and using one would let us avoid the use of Recompose as well as having to wrap multiple layers of HOCs manually. So why did we still choose the HOC route? Let's take a look at what a render prop version of `withFooSelector` might look like:

```
import fooSelector from 'modules/selectors/foo';

// A render prop implementation of `fooSelector` dependency injection
const WithFooSelector = ({ render }) => render(fooSelector);

export default WithFooSelector;
```

A render prop implementation of `fooSelector` dependency injection

And here's what FooComponent would look like in the render prop scenario:

```
import { connect } from 'react-redux';

import WrappedFoo from 'modules/decorators/WrappedFoo';

const FooComponent = ({ foo }) => (
 // Still just JSX here
);

export const mapStateToProps = (nextReduxState, nextOwnProps) => {
 ...
 return {
   foo: nextOwnProps.fooSelector.getFoo(nextReduxState),
   // other store-based props here...
 };
};

const ConnectedFoo = connect(mapStateToProps)(FooComponent);

//  Our original component, but with our render prop component providing `fooSelector`
const WrappedFoo = () => (
 <WithFooSelector
   render={fooSelector => <ConnectedFoo fooSelector={fooSelector} />}
 />
);

export default WrappedFoo;
```

So what's changed here? Well, instead of an HOC, we now have these `WithFooSelector` and `WrappedFoo` components that seem to be doing what `withFooSelector` was doing, only now we have both a render prop component and the instantiation of it to deal with. With the HOC approach, we only needed one wrapper component and we were done. This is a pretty minor tradeoff, so it seems like render props might have been a fine choice here, but we chose to avoid having to create a new `Wrapped*` component for every case where we needed `fooSelector` injected. And while this extra wrapping could be abstracted away to reduce boilerplate, we'd likely end up in more or less the same place as we were with the HOC.

### HOCs as Dependency Injection

![](https://miro.medium.com/max/60/1*NAzL43YCnLbDREgdsww1Hg.jpeg?q=20)

![](https://miro.medium.com/max/556/1*NAzL43YCnLbDREgdsww1Hg.jpeg)

With this approach, we've shown that without any real DI framework, we can get many of the benefits of DI, including dependency swap-ability and unit testability. If we had a second `fooSelector` that we wanted to use with our component in a different part of the app, we could easily do so without modifying any of the internals of our component, and certainly without adding any hairy conditional logic! We also were able to turn more of our core application logic into pure functions, making writing tests a breeze.

One downside is that we now have a layer of indirection in our code that could hurt readability: before, a reader could see the exact module that `fooSelector` was coming from without any ambiguity. Now, the reader needs to look at the implementation of `withFooSelector` to see exactly where `fooSelector` is coming from. So far, this hasn't manifested as a real pain point for us during development, but we'll continue to evaluate our assumptions as we use this pattern more.

We're also now exposing ourselves to the risk of our mocked version of `fooSelector` in our unit test drifting from the real implementation in `modules/selectors/fooSelector`. This is a common risk of mocking in unit tests, and one that wouldn't be truly solved even with Jest's manual mocks. One approach we may go with to lower this risk is to use [fakes](https://blog.pragmatists.com/test-doubles-fakes-mocks-and-stubs-1a7491dfa3da) rather than mocks: this would involve us maintaining a single fake implementation of `fooSelector` in our repo, and injecting *that* into components in our tests. While we'd still be exposed to the risk of the fake drifting from the real implementation, we'd have consolidated that risk into a single module in our repo.

So is DI through HOCs a worthwhile abstraction? We think it's promising enough to continue exploring, and we're optimistic that we can work to minimize the shortcomings while still preserving purity and testability in our code. We'll report back [here](https://blog.koan.co/) with the results!