Testing

This part describes, how you can improve your app's stability with different testing tools

Unit Testing

The first and the most popular option is unit-testing of your JavaScript modules. React Native Starter comes with ready-to-use Jest integration, which you can use to test components, classes and functions.

Let's talk about testing each of them.

Testing Views

The best way to test if your views are rendered correctly is to create a view snapshot and compare the new snapshots each time. This way you can see when your views are changing or not.

Let's pretend you have a button component that can take a string or an image as a children component and calls onPress handler when TouchableOpacity inside this component is clicked. So your button component looks something like this:

components/Button.js
export const Button = ({ children, onPress }) => (
  <View style={styles.container}>
    <TouchableOpacity onPress={onPress}>
      {children}
    </TouchableOpacity>
  </View>
);

And what you want to test here are that:

  1. Button renders correctly with image and with a text.

  2. onPresshandler is fired when TouchableOpacity has been pressed.

Let's create a test file called Button.spec.js. By default, jest looks for all the files named as *.spec.js, *.test.js and for files placed under the __tests__ folder.

components/Button.spec.js
import React from 'react';
// Import enzyme's shallow renderer
import { shallow } from 'enzyme';

// Describes a test group
describe('Button', () => {
  it('renders correctly with a text', () => {
    // Render our button with a text inside
    const wrapper = shallow(<Button>test</Button>);

    // Check that the rendered button matches
    // previously saved snapshot
    expect(wrapper).toMatchSnapshot();
  });

  it('renders correctly with an image', () => {
    // Render our button with an image inside
    const wrapper = shallow(
      <Button>
        <Image src={require('something')} />
      </Button>,
    );

    // Check that the rendered button matches
    // previously saved snapshot
    expect(wrapper).toMatchSnapshot();
  });

  it('handles onPress', () => {
    // Create a mock function to pass as a handler
    const onPress = jest.fn();

    // Render our button with an image inside
    const wrapper = shallow(<Button onPress={onPress}>test</Button>);

    // Find a TouchableOpacity and press it
    wrapper
      .find('TouchableOpacity')
      .first()
      .props()
      .onPress();

    // Check that our handler have been called 1 time
    expect(onPress).toHaveBeenCalledTimes(1);
  });
});

Learn more about Unit-testing here: https://jestjs.io/docs/en/tutorial-react-native

End to end testing

We integrated Detox framework for running end2end tests for your app. We also integrated it with jest, so you can write your tests in the familiar environment.

High velocity native mobile development requires us to adopt continuous integration workflows, which means our reliance on manual QA has to drop significantly. Detox tests your mobile app while it's running in a real device/simulator, interacting with it just like a real user.

The most difficult part of automated testing on mobile is the tip of the testing pyramid - E2E. The core problem with E2E tests is flakiness - tests are usually not deterministic. We believe the only way to tackle flakiness head on is by moving from black box testing to gray box testing. That's where Detox comes into play.

  • Cross Platform: Write cross-platform tests in JavaScript. Currently supports iOS, Android is nearly complete. View the Android status page.

  • Runs on Devices (not yet supported on iOS): Gain confidence to ship by testing your app on a device/simulator just like a real user.

  • Automatically Synchronized: Stops flakiness at the core by monitoring asynchronous operations in your app.

  • Made For CI: Execute your E2E tests on CI platforms like Travis without grief.

  • Test Runner Independent: Use Mocha, AVA, or any other JavaScript test runner you like.

  • Debuggable: Modern async-await API allows breakpoints in asynchronous tests to work as expected.

Let's pretend you want to test your that your login flow works and verify it with Detox.

First, create a file called login.spec.js under the e2e folder and add the following code there:

e2e/login.spec.js
describe('Login flow', () => {
  it('should login successfully', async () => {
    await device.reloadReactNative();
    await expect(element(by.id('email'))).toBeVisible();
      
    await element(by.id('email')).typeText('john@example.com');
    await element(by.id('password')).typeText('123456');
    await element(by.text('Login')).tap();
      
    await expect(element(by.text('Welcome'))).toBeVisible();
    await expect(element(by.id('email'))).toNotExist();
  });
});

Then, simply open your console and type

e2e:build && e2e:test

That's it! You should see the iOS simulator opened and your app tested.

Last updated