How to test third-party React Native components using React Native Testing Library
Tags: reactnative, testing, javascript, beginners
Published: 2022-07-15 22:48:10

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](https://callstack.github.io/react-native-testing-library/docs/api#fireevent) 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:

```jsx
//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;
```

```js
//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];
})
```

`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:

```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];
  console.log("picker props: ", picker.props)
})
```
Here's a screenshot of the log:

![Screenshot 1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8x0ykeqxadelzgevbeu2.png)

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:

```ts
fireEvent(element: ReactTestInstance, eventName: string, ...data: Array<any>): void
```

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m7ozmh43alb9yv7b4sw8.png)

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

```jsx
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;
```

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

```js
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");
})
```

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:

```jsx
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;
```

```js
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();
})
```

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] (https://www.buymeacoffee.com/hmcodes)

## Sources

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

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