Testing React Native app with React Test Renderer, TypeScript and HOC

Jun Kaneko
3 min readApr 10, 2019

--

Expo.js’ Jest introduction document is useful to start the unit testing of the Expo app. But soon I was lost in the deep forest of the various testing libraries for React.

react-test-renderer seems to be promising, but the documentation is a kind of bare minimum. TypeScript examples were also fewer than the web. So here is my note, hoping to help someone like me.

The full example is at the bottom of this article.

Simple React Component to test

Let’s consider to test this simple react component.

import * as React from 'react';
import { Component } from 'react';
import { Text } from 'react-native';
interface IProps {
count?: number;
}
interface IState {
count: number;
}
class TestClass extends Component<IProps, IState> {state: IState = {
count: this.props.count || 0,
};
render() {
return(<Text testID='counter'>{this.state.count}</Text>);
}
}
export default TestClass;

The first test case to check the initial state look like this.

it("should have counter element", () => {
let root = renderer.root;
const counter = root.findAll(
(el) => el.props.testID === 'counter'
);
expect(counter).toHaveLength(1);
});

TypeError: Cannot read property ‘createElement’ of undefined

The first time I run the test, I got into “TypeError: Cannot read property ‘createElement’ of undefined” error. It was because of this React import statement.

import React, { Component } from 'react';

With TypeScript, it should be:

import * as React from 'react';
import { Component } from 'react';

Duplicated length when findAll()

Then when I tried to find the element by testID, the length was 2 instead of 1 even the dumped JSON shows only one element.

it("should have one counter element", () => {
let root = renderer.root;
const tree = renderer.toJSON();
console.log(tree)
const counter = root.findAll(
(el) => el.props.testID === 'counter'
);
expect(counter).toHaveLength(1);
});

After googling around, I found that the parent Component also would have the same testID so the component type had to be specified at the same time.

el.props.testID === 'counter' && el.type === 'Text'

instance.setState() to test the state change

How about checking the state change? I thought that should relate to .getInstance() in the documentation, but actually, I could get the instance directory from the root element.

it("should setState the counter to 1", () => {
let root = renderer.root;
const instance = root.instance;
instance.setState({ count: 1 })
const counter = root.findAll(
(el) => el.props.testID === 'counter' && el.type === 'Text'
);
expect(counter[0].children).toEqual(['1']);
});

Test HOC (Higher-Order Components)

Finally, the test for Higher Order Components. Just imagine a HOC which gets some parameters and do something to pass down the processed props to the wrapped component. This is a useless HOC example but just to see how to test it.

import * as React from 'react';
import { Component } from 'react';
interface IProps {}
interface IState {}
function TestHoc(params: any) {
return (WrappedComponent: any) => {
return class CountFactory extends Component<IProps, IState> { render() {
return (<WrappedComponent {...params} />);
}
}
}
}
export default TestHoc;

All you need to do is to compose the HOC before TestRenderer.create.

describe('Set preps With HOC', () => {  it("should preset counter to 2", () => {
const CountFactory = TestHoc({ count: 2 })(TestClass);
renderer = TestRenderer.create(
<CountFactory />
);
let root = renderer.root;
const counter = root.findAll(
(el) => el.props.testID === 'counter' && el.type === 'Text'
);
expect(counter[0].children).toEqual(['2']);
});

});

Conclusion

The final shape of the unit test would like as below.

These look quite obvious once they’ve been written but it just took a bit of time to search around the various documents and tips.

--

--

Jun Kaneko

Based in London and Devon, I love surfing and technology.