Simple React Native Jest Testing Example

This project is built for Android. It may work for iOS, but hasn't been tested there.

Instructions for creating a simple React Native project are here. I followed those to create my project template:

npx react-native init ReactNativeJestTestingExample

This creates a directory, ReactNativeJestTestingExample, containing a simple React Native app that's ready to run.

After getting everything set up, the instructions say

cd "ReactNativeJestTestingExample" && npx react-native run-android

The instructions that you see there from the CLI are incorrect, and you'll see an error if you follow them. To avoid that, open a second terminal and npx react-native start to run Metro server.

After creating the application using the standard boilerplate, I add a component and modified the app quite a bit, because I wanted to create tests for a bug that I'd run across in my development work.

The app has been checked in with a bug that will cause the app to crash on Android.

the template test

Here's the React Native testing documentation.

https://reactnative.dev/docs/testing-overview#structuring-tests

Our template project has a test built in. It's here: __tests__/App-test.js.

Run the test: npm test. You must use npm and not npx.

This is the output:


> reactnativejesttestingexample@0.0.1 test
> jest

 PASS  __tests__/App-test.js (5.065 s)
  ✓ renders correctly (3538 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        5.099 s
Ran all test suites.

The App-test.js test doesn't do much:

it('renders correctly', () => {
  renderer.create(<App />);
});

another component test

Here's a more meaningful test.

https://reactnative.dev/docs/testing-overview#structuring-tests

Replace the current display content with something more functional, a KeyboardAvoidingView with some text inputs.

I added styled-components, because I wanted to add a test for a problem that I had experienced in a real project.

Unfortunately, I ran into a problem during the install. I wound up deleting node_modules and installing all dependencies via yarn, which worked:

yard add react react-native styled-components
yarn add --dev @testing-library/jest-native

Now I can install the app, run it, and test it manually:

(Terminal 1)

npx react-native start

(Terminal 2)

npx react-native run-android

Here's a screenrecording of the app that demonstrates how the KeyboardAvoidingView works.

We're on our way to a functioning form with TextInputs. But there are no tests, so every time you tweak something, you run the risk of unknowingly breaking the app. And there's no way to easily exercise any code that you add.

In fact, the code as it is contains a bug, but you can't tell that, because the demo app doesn't exercise all the code in the FlexibleContent component. The code in App.js doesn't define isPortal prop on FlexibleContent, so the styled KeyboardAvoidingView has height set to auto:

      <FlexibleContent
      style={{
        backgroundColor: '#EFEFEF',
        padding: 0,
        height:'auto'
        }
      }
      >

It's not really clear that auto height is valid, but the app doesn't complain.

What if FlexibleContent has isPortal set to true?

      <FlexibleContent
      style={{
        backgroundColor: '#EFEFEF',
        padding: 0,
        height:'auto'
        }
      }
      isPortal={true}
      >

In that case, the KeyboardAvoidingView will get height set to 100px:

  <FlexibleContentKeyboardAvoidingView
  behavior="padding"
  height={this.props.isPortal ? '100px' : 'auto'}
  >

and the app crashes on Android. You might see a redbox error with Error while updating property 'height' in shadow node of.... Or the app just might crash without an explanation. On Android, you'll see a stack trace that starts with the unhelpful java.lang.IllegalStateException: com.facebook.react.uimanager.IllegalViewOperationException: ViewManager for tag 59 could not be found..

What happened was that height is not allowed to be a string like 100px. It's easy to see how a developer might accidentally set height to this string value. The styled-components library allows you to use heights like 100px with pixels and other units attached. We're mixing styled components and regular React Native components, which can lead to a little bug like this. It is surprisingly difficult to debug.

The tests in FlexibleContent-test.js were written to exercise both code paths: the one where the KeyboardAvoidingView has height set to 100px and the one where it's set to auto. Unfortunately, I see no way to just make these tests crash when an invalid height is passed in. If the developer is so foolish as to set height to the string "monkey butt", and then write a test that expects the height to be "monkey butt", then the test will pass, and a bug will exist in the code.

You just have to know the valid values for a View's height property in React Native. Notice the documentation states:

All dimensions in React Native are unitless, and represent density-independent pixels.

You can use percentage dimensions in a style, like so: <View style={{ height: '100%' }}>.

It's actually quite difficult to find what the allowed values are for height and width when searching through the React Native documentation. :/