DEV Community

Hodeem
Hodeem

Posted on • Edited on

How to test third-party React Native components using React Native Testing Library

Testing third-party React Native components using Jest and React Native Testing Library can be really frustrating. I remember tryin every event I saw in the docs to no avail. press, click, scroll, the whole nine yards.

However, through blood, sweat and tears trial and error I managed to figure out a general approach that works and I wanted to share it to save others the stress.

I'm going to illustrate the approach by demonstrating simple tests on a component from the react-native-picker-select package.

Locating the component in the tree

The first step for most tests is to locate the component(s) that will be involved. The problem is that not every third-party component provides the option to directly set a prop such as testID, so I had to get creative.

We start by wrapping the component within a pair of <View> tags and setting a prop on the parent <View>, such as testID. Afterwards, we can locate the component by accessing the <View>'s children. Here's some sample code to demonstrate:

//CustomPickerSelect.jsx

import React from 'react';
import { View } from 'react-native';
import RNPickerSelect from 'react-native-picker-select';

const CustomPickerSelect = () => {

    return (
        <View testID='picker-select-parent'>
            <RNPickerSelect
                onValueChange={() => { }}
                items={[]}
            />
        </View>
    )
}

export default CustomPickerSelect;
Enter fullscreen mode Exit fullscreen mode
//CustomPickerSelect.test.js

import { render } from '@testing-library/react-native';
import CustomPickerSelect from './CustomPickerSelect';

test('Testing CustomPickerSelect', () => {
  const { getByTestId } = render(<CustomPickerSelect />)

  const picker = getByTestId("picker-select-parent").children[0];
})
Enter fullscreen mode Exit fullscreen mode

picker is the ReactTestInstance that represents the third-party component RNPickerSelect in the rendered tree.

Firing events

For third-party components, the names of the events (or event handlers) which elicit a response are usually listed in their props. The props are usually available in the documentation, but this is not always the case. The documentation may also be outdated, so it helps to see for yourself which props are available by logging the .props property of the ReactTestInstance. Let's do this now:

import { render } from '@testing-library/react-native';
import CustomPickerSelect from './CustomPickerSelect';


test('Testing CustomPickerSelect', () => {
  const { getByTestId } = render(<CustomPickerSelect />)

  const picker = getByTestId("picker-select-parent").children[0];
  console.log("picker props: ", picker.props)
})
Enter fullscreen mode Exit fullscreen mode

Here's a screenshot of the log:

Screenshot 1

Let's take a brief detour to discuss the main tool that we'll be using: fireEvent. The fireEvent method provided by React Native Testing Library has the following syntax:

fireEvent(element: ReactTestInstance, eventName: string, ...data: Array<any>): void
Enter fullscreen mode Exit fullscreen mode

If the meaning of the data parameter is unclear to you, it actually represents the argument(s) that will be passed to the event handler that is being invoked.

The challenge is determining the right value to use for the eventName argument. This is where we reference the props for the ReactTestInstance that were logged earlier. The event handlers are usually prefixed by "on-", for example onPress.

Here are the event handlers for our ReactTestInstance:

Screenshot 2

Let's make a few additions to the CustomPickerSelect.jsx component to facilitate the testing of the onValueChange event handler:

import React, { useState } from 'react';
import { View } from 'react-native';
import RNPickerSelect from 'react-native-picker-select';

const CustomPickerSelect = () => {

    const [value, setValue] = useState("")

    return (
        <View testID='picker-select-parent'>
            <RNPickerSelect
                value={value}
                onValueChange={(newValue) => { setValue(newValue) }}
                items={[]}
            />
        </View>
    )
}

export default CustomPickerSelect;
Enter fullscreen mode Exit fullscreen mode

Now let's invoke the onValueChange event handler and check to make sure that everything works as expected:

import { render, fireEvent } from '@testing-library/react-native';
import CustomPickerSelect from './CustomPickerSelect';


test('Testing CustomPickerSelect', () => {
    const { getByTestId } = render(<CustomPickerSelect />)

    const picker = getByTestId("picker-select-parent").children[0];
    fireEvent(picker, 'onValueChange', "A");
    expect(picker.props.value).toBe("A");
})
Enter fullscreen mode Exit fullscreen mode

It's generally not encouraged to assert on implementation details, but it can't hurt to know how to do this ;).

If you want to conduct other tests, you can make the necessary changes. For example, here's some code to test if a callback function passed in as a prop to the CustomPickerSelect component is invoked:

import React, { useState } from 'react';
import { View } from 'react-native';
import RNPickerSelect from 'react-native-picker-select';

const CustomPickerSelect = ({ setValueCallback }) => {

    const [value, setValue] = useState("")

    return (
        <View testID='picker-select-parent'>
            <RNPickerSelect
                value={value}
                onValueChange={(newValue) => { 
                    setValue(newValue);
                    setValueCallback(newValue);
                }}
                items={[]}
            />
        </View>
    )
}

export default CustomPickerSelect;
Enter fullscreen mode Exit fullscreen mode
import { render, fireEvent } from '@testing-library/react-native';
import CustomPickerSelect from './CustomPickerSelect';


test('Testing CustomPickerSelect', () => {

  const mockCallback = jest.fn();

  const { getByTestId } = render(
    <CustomPickerSelect 
       setValueCallback={mockCallback} 
    />
  )

  const picker = getByTestId("picker-select-parent").children[0];
    fireEvent(picker, 'onValueChange', "A");
    expect(mockCallback).toHaveBeenCalled();
})
Enter fullscreen mode Exit fullscreen mode

That's it for now. Thanks for reading and share this with a friend if this helped.

If you liked this article, then please support me

Sources

https://www.npmjs.com/package/react-native-picker-select

https://callstack.github.io/react-native-testing-library/docs/api#fireevent

Top comments (0)