Riccardo Coppola

Use proxyquire to mock your React components

July 18, 2016

Testing React components has become trivial as more and more of them are now stateless functions.

The new shallow renderer and tools like Enzyme make it possible to render a component “one level deep” when testing, without even rendering it in some sort of DOM, while still having a great api to traverse the components’ output and assert facts on its behaviour.

Given all these great tools that we have, what happens when we have a container component that renders sub-components (child components) based on some sort of rule/logic?
How do we handle those dependencies to keep our unit tests free from dependencies?

A HomePage component example

import React from 'react';
import Header from './Header';
import Sidebar from './Sidebar';
import Footer from './Footer';
import { Oops } from './Oops'; // Importing a non-default export

export default function HomePage({ data, error }) {
  let component = (
    <div>
      <Header links={data.headerLinks} />
      <Sidebar />
      <Footer links={data.footerLinks} />
    </div>
  );
  
  if (error) {
    component = (
      <Oops message={error.message} />
    );
  }
  
  return component;
}

The example renders Header, Footer and Sidebar if there is no error, Oops otherwise.

Testing the HomePage component

When testing the HomePage component, we’ll probably shallow render it and check that it renders the right components based on its props.

Why shallow render is a good thing?
From the Enzyme docs:

Shallow rendering is useful to constrain yourself to testing a component as a unit, and to ensure that your tests aren’t indirectly asserting on behavior of child components.

The code:

import React from "react";
import { shallow } from "enzyme";
import HomePage from "../components/HomePage";
import Header from "../components/Header";
import Sidebar from "../components/Sidebar";
import Footer from "../components/Footer";
import { Oops } from "../components/Oops";

describe("The HomePage component", function () {
  describe("when there is no error", function () {
    it("should render the page", function () {
      const props = {
        data: {
          links: "",
        },
      };
      const output = shallow(<HomePage {...props} />);

      expect(output.find(Header)).to.have.lengthOf(1);
      expect(output.find(Sidebar)).to.have.lengthOf(1);
      expect(output.find(Footer)).to.have.lengthOf(1);
      expect(output.find(Oops)).to.have.lengthOf(0);
    });
  });

  describe("when there is an error", function () {
    it("should render the Oops", function () {
      const props = {
        error: {
          message: "blown up",
        },
      };
      const output = shallow(<HomePage {...props} />);

      expect(output.find(Header)).to.have.lengthOf(0);
      expect(output.find(Sidebar)).to.have.lengthOf(0);
      expect(output.find(Footer)).to.have.lengthOf(0);
      expect(output.find(Oops)).to.have.lengthOf(1);
    });
  });
});

This test is importing the real Header, Footer, Sidebar and Oops components, creating a dependency when it should just assume that they are behaving the way they are supposed to, so that if tomorrow the Header component blows up, the HomePage tests won’t be affected (the Header tests will, though!).

Introducing proxyquire

Proxyquire

Proxies nodejs’s require in order to make overriding dependencies during testing easy while staying totally unobstrusive.

This is how it comes to our rescue:

import React from 'react';
import { shallow } from 'enzyme';
import proxyquire from 'proxyquire';

proxyquire.noCallThru();

const Header = () => <div></div>; // stub component
const Sidebar = () => <div></div>; // stub component
const Footer = () => <div></div>; // stub component
const Oops = () => <div></div>; // stub component

// Require the component to test (HomePage) through proxyquire
// and pass it the stubbed dependencies
const HomePage = proxyquire('../components/HomePage', {
  './Header': Header,
  './Sidebar': Sidebar,
  './Footer': Footer,
  './Oops': { Oops }
}).default;

describe('The HomePage component', function () {
  describe('when there is no error', function () {
    it('should render the page', function () {
      const props = {
        data: {
          links: ''
        }
      };
      const output = shallow(<HomePage {...props} />);
      
      expect(output.find(Header)).to.have.lengthOf(1);
      expect(output.find(Sidebar)).to.have.lengthOf(1);
      expect(output.find(Footer)).to.have.lengthOf(1);
      expect(output.find(Oops)).to.have.lengthOf(0);
    });
  });
  
  describe('when there is an error', function () {
    it('should render the Oops', function () {
      const props = {
        error: {
          message: 'blown up'
        }
      };
      const output = shallow(<HomePage {...props} />);
      
      expect(output.find(Header)).to.have.lengthOf(0);
      expect(output.find(Sidebar)).to.have.lengthOf(0);
      expect(output.find(Footer)).to.have.lengthOf(0);
      expect(output.find(Oops)).to.have.lengthOf(1);
    });
  });
});

Code analysis:

  • On line 5:

By default proxyquire calls the function defined on the original dependency whenever it is not found on the stub.

We don’t want to call any function not defined in our component, so we define a more strict behaviour.

  • Lines 7-10: We define our mock components as simple stateless functions. We are not interested in what the component behaviour is, we just want to test that it receives the right props (that is being rendered correctly by the component under test).
  • Lines 12-17: It is here that we pass our mocks to the HomePage component. We are not importing it at the top of the file, we are instead requiring it through proxyquire, passing it a set of dependencies that we want to mock.

We don’t have to mock all the HomePage dependencies, only the ones that we want to control and we get back the HomePage component with all those dependencies replaced by our stubs.

A note on ES6 modules

At the top of HomePage.jsx we import the component’s dependencies:

...
import Footer from './Footer';
import { Oops } from './Oops';
...

The first line is importing a default export (export default function () { ··· }), the second is importing a named export (export function Footer () {...});

// math.js
export function sum(a, b) {
  return a + b;
}

export default function sub(a, b) {
  return a - b;
}

// main.js

import sub from './math';
import { sum } from './math'; // or const sum = require('./math').sum;

Given the code above, the result of the exports is an object that looks like this:

import * as math from "./math";

console.log(math);

// {
//   default: function sub (a, b) {...},
//   sum: function sum (a, b) {...}
// }

This is why on line 19 in HomePage.proxy.spec.jsx we take the default object from the result returned by proxyquire: that is where the exported function is.

The syntax import Footer from './Footer' hides the fact that under the hood the imported object is whatever is found inside the default property of the object returned by ./Footer.

Wrapping up

Proxyquire can mock all sorts of dependencies, this is just an example that applies to React; have a look at the examples to see how powerful it can be.

Useful links:


Notes on web development, life, learning and the world.