React-Intl-Unit-Test

Mocking react-intl package for unit testing

Previously, I wrote about unit testing components which use react-intl package. If you did not inject an intl object to the component, it would throw errors while running tests. The solution was to create a factory method which wrapped the component with an IntlProvider and ref the component. This solution is handy if you are not using enzyme, otherwise since your mounted/shallowed component will be the IntlProvider it is going to be hard to manipulate the state and/or props of our component. 

Jest provides a neat way to mock the import statements. This way, while unit testing our application we can focus on our app, rather than trying to provide a mock context for our components. To do this, we simply have to tell jest what to load in case of an imported statement. Here is how we do it at work. Within the root of our application we have the config file:

jest.config.js
module.exports = {
  // ...
  moduleNameMapper: {
    'react-intl': '<rootDir>/internals/mocks/react-intl.js'
  }
}

This config file will tell jest to wrap the imported module with the provided file. So all statements that use import * from 'react-intl' will be wrapped with this code and we can modify the behaviour however we wish. In our case, we our mock was something like:

internals/mocks/react-intl.js
import React, { Component } from 'react'
const Intl = require('react-intl')

const identityFunction = i => i

// Here goes intl context injected into component, feel free to extend
const intl = {
  formatDate: identityFunction,
  formatTime: identityFunction,
  formatRelative: identityFunction,
  formatNumber: identityFunction,
  formatPlural: identityFunction,
  formatHTMLMessage: identityFunction,
  now: identityFunction,
  formatMessage: o => o.defaultMessage,
}

// Mock following implementations so that they do not break
Intl.injectIntl = (Node) => {
  // If we are injecting a Class, return a class, otherwise return the functional component
  // This is useful in cases where you are shallowing a component to test a single method.
  if (Node.prototype instanceof Component) {
    return class extends Node {
      static defaultProps = {
        ...(Node.defaultProps || {}),
        intl
      }

      render () {
        return <Node {...this.props} />
      }
    }
  } else {
    return props => <Node {...props} intl={intl} />
  }
}

Intl.FormattedMessage = ({ defaultMessage }) => <span>{defaultMessage}</span>
Intl.FormattedHTMLMessage = ({ defaultMessage }) => <span>{defaultMessage}</span>

module.exports = Intl

We are basically loading the react-intl package, changing the injectIntl and FormattedMessage behaviour and returning the modified object. Now in your tests you will be able render/mount your components without the need of wrapping them. Much cleaner code, much more fun unit testing! Feel free to comment below if you have any thoughts on this.