React Native 45 Errors in Jest

At Build.com, a co-worker and I are in the process of updating our app to the most recent version of React Native, 0.45.1. We’re currently working to fix up our tests in Jest and I thought I’d share with you some issues that we’ve discovered.

How is testing different in React Native 45?

The short answer is that it is not that different but a number of things are currently broken or deprecated including:

  • Enzyme
  • React.createClass
  • PropTypes

Why doesn’t Enzyme work with React Native 45

Enzyme is a testing utility that makes it easy for you to test your components. It is commonly used to perform a “shallow” render of a component. This means that the component is rendered in a virtual DOM but the dependencies of the component are not followed so only the first layer is rendered.

React Native 43+ uses the alpha version of React 16 as one of its dependencies. Enzyme has taken a pretty firm stance that they will not support alpha versions of React.

If you are using React Native 43+, you cannot use Enzyme as the rendering library for your tests. The replacement we’ve chosen is react-test-renderer. They both provide pretty similar functionality overall, but Enzyme is a bit more convenient.

Compare the same tests in Enzyme and in react-test-renderer:

describe('MyComponent', () => {
  // Enzyme
  it('should render', () => {
    const tree = shallow(<MyComponent {...defaultProps} />);
    expect(tree).toMatchSnapshot();
  });
 
  // react-test-renderer
  it('should render', () => {
    const tree = require('react-test-renderer').create(
      <MyComponent {...defaultProps} />
    ).toJSON();
    expect(tree).toMatchSnapshot();
  });
});
 
describe('MyComponent function tests', () => {
  // Enzyme
  it('setScreenTrackingInformation', () => {
    const tree = shallow(<MyComponent {...defaultProps} />)
      .instance()
      .setScreenTrackingInformation();
    expect(tree).toMatchSnapshot();
  });
 
  // react-test-renderer
  it('setScreenTrackingInformation', () => {
    const tree = require('react-test-renderer').create(
      <MyComponent {...defaultProps} />
    )
      .getInstance()
      .setScreenTrackingInformation();
    expect(tree).toMatchSnapshot();
  });
});

Note: for more information about the repeated require('react-test-renderer') see my prior blog post on the topic.

How has React.createClass changed?

React.createClass has been deprecated from React 16. If you try to use it, you will receive the following error:

Warning: React.createClass is no longer supported. Use a plain JavaScript class instead. If you’re not yet ready to migrate, create-react-class is available on npm as a drop-in replacement. (https://fb.me/migrating-from-react-create-class)

I don’t think that createClass was very common for most people in React Native since that environment already allows the use of the JavaScript class or arrow function construct. I believe another reason to use it was that it allowed you to use mixins.

However, Jest allows you to create mock files in a /__mocks__ folder. These mocks were useful for mocking libraries that you may be using.

I found that createClass could be convenient to use when mocking components. If you have a lot of mocks using createClass, you can simply switch to the nearly equivalent function, createReactClass, in the new create-react-class library. You can also switch to using a component class.

Here’s an example with all three styles of mocked components:

import React from 'react';
import createReactClass from 'create-react-class';
 
class Text extends React.Component {
  render() {
    return React.createElement('Text', this.props, this.props.children);
  }
}
 
module.exports = {
  List: React.createClass({  // the deprecated method
    scrollTo: jest.fn(),
    render() {
      return React.createElement('ListView', this.props, this.props.children);
    },
  }),
  Page: createReactClass({  // using the new create-react-class library
    render() {
      return React.createElement('Page', this.props, this.props.children);
    },
  }),
  Text,  // exporting a component class
};

Note, in the example above, I show how to create a mock component using the class style. You’ll note that I’m using createElement rather than JSX. I tried JSX but for some reason, it put Jest into an infinite loop and caused a stack overflow. I’m not sure what the problem is but this was a pretty simple alternative.

Unfortunately, you will likely still see the “React.createClass is no longer supported” error. React Native 45 is still using the function within the library. So if I run jest from the command line, I see that error at least once for every time that my tested component is mounted. Hopefully, they will fix that error soon.

How have PropTypes changed in React Native 45?

There have two major changes to the way prop types work in recent versions of React Native:

  1. React.PropTypes has been moved into a separate library, prop-types, and will be removed from React 16
    • As I understand it, this change was made because not everyone uses PropTypes to check their data types (for example, some use Flow instead). This allows the React core library to be as light as possible
  2. ReactNative.View.propTypes has been deprecated and you should now use ReactNative.ViewPropTypes

Using the wrong version of prop types will trigger one of the following two warnings:

Warning: PropTypes has been moved to a separate package. Accessing React.PropTypes is no longer supported and will be removed completely in React 16. Use the prop-types package on npm instead. (https://fb.me/migrating-from-react-proptypes)

Warning: View.propTypes has been deprecated and will be removed in a future version of ReactNative. Use ViewPropTypes instead.

Here are what those changes look like:

import { PropTypes } from 'react'; // triggers warning
import PropTypes from 'prop-types'; // use this instead
 
import {
  View, // using View.propTypes will trigger the warning
  ViewPropTypes, // use this instead
} from 'react-native;

Unfortunately, as with the createClass error, the React Native library is still using the old forms of prop types so running Jest will generate these errors even if your code doesn’t have any issues.

How do I hide the Jest errors created by the React Native library?

With all of the changes and the errors/warnings that are triggered by the React Native library, it can be really hard to find any errors that were caused by your code.

To help eliminate some of the extraneous errors, try running Jest in silent mode:

$ jest --silent

Leave a Comment