Setting up Appium for React Native e2e - Automation Testing
Posted 14 May, 2020
#Technology

Setting up Appium for React Native e2e - Automation Testing

End to End (e2e) testing is a technique that helps ensure the quality of mobile apps  in an environment as close to real life as possible, testing the integration of all the pieces that integrate a software automatically. On a mobile app, this could be particularly useful given the diversity of real devices and platforms our software is running on top of.

Due to the cross-platform nature of React Native, e2e testing proves to be particularly messy to work on. As a result, we have to write all of our tests bearing this in mind, changing the way we access to certain properties or query elements no matter the tool we use for connecting to it. Still, automation testing tools like Appium and WebdriverIO allow us to work over a common and somewhat standard interface.

The following instructions assume we already have a React Native app built with expo, and use Jest for our unit-testing solution.

Disclaimer: The following instructions are based on a Windows machine running an android emulator.  output/commands may vary slightly on different architectures.

Setting Up Appium

  • Install required dependencies
$ npm i -D webdriverio babel-plugin-jsx-remove-data-test-id concurently

WebdriverIO will work as our “client” for the appium server in the case of JS. There is more to come regarding how to use other clients such as python.

babel-plugin-jsx-remove-data-test-id will help us remove unwanted accessibilityLabels from our mobile app, since that’s the preferred way of targeting elements for both IOS and Android platforms

concurrently will help us automate the running of appium server and jest to do our e2e tests

  • Install Appium Doctor
$ npm install appium-doctor -g

This will help us identify if we have all of the needed dependencies to correctly run appium in an emulator.

  • Run Appium Doctor

Depending on the host OS we want to test in, we could run:

$ appium-doctor --android

or

$ appium-doctor --ios

For this particular case I’ll be running the android version. This will prompt some output on the console. If we have all the required dependencies installed we should see a message similar to the following:

If not all necessary dependencies are met at this point, instead of checkmarks before any given item you’ll see a red X symbol. Check the end of the input for more information on how to fix the particular Issues you’re prompted.

We’re not going to fix the optional requirements that appium-doctor prompts for the time being, feel free to go over those once you have the testing solution working.

  • Run Appium

By this point, you should be able to run your appium server without any issues, in order to do so just type

$ appium

You should see something similar to

If you do so, congrats! you have correctly set up appium.

Now, let's set up our tests.

Write tests once, run in any platform

One of the key features of react native is its ability to write code once and run it in both iOS and Android,  that is what we want our tests to behave in the same way. There are some limitations for this, since the only way we can write a selector for both platforms is through the accessibilityLabel attribute in react native.

This may become an issue if your mobile app depends on accessibility features.  Make sure to use correct, semantic and descriptive accessibility labels at any place you intend to use them.

If a great accessibility is not on the scope of your current project (it should), you can use accessibilityLabel as a perfect target for querying your elements, just make sure you don’t accidentally worsen the experience of people using screen readers or any other assistive technology.

In order to do this, we’re going to configure our babel setup to remove the accessibility labels whenever we build for production:

/// babel.config.js

module.exports = function() {  
  return {
    presets: ['babel-preset-expo'],
    env: {
      production: {
        plugins: [
          [
            'babel-plugin-jsx-remove-data-test-id',
            { attributes: 'accessibilityLabel' },
          ],
        ],
      },
    },
  };
};

Let’s write our first test now:

I’ve created a called LoginTest.spec.js inside a new folder called e2e. Inside the file you can find the following:

// myapp/e2e/LoginTest.spec.js

import wdio from 'webdriverio';

jasmine.DEFAULT_TIMEOUT_INTERVAL = 60000;

const opts = {  
  path: '/wd/hub/',
  port: 4723,
  capabilities: {
    platformName: 'android',
    deviceName: 'emulator-5554',
    app: 'my-app-name.apk',
    automationName: 'UiAutomator2',
  },
};

describe('Expo test example', function() {  
  let client;
  beforeAll(async function() {
    client = await wdio.remote(opts);
    await client.pause(3000);
    const pack = await client.getCurrentPackage();
    const activity = await client.getCurrentActivity();
    await client.closeApp();
    await client.startActivity(pack, activity); //Reload to force update
    await client.pause(3000);
  });

  afterAll(async function() {
    await client.deleteSession();
  });

  it('should allow us to input username', async function() {
    // Arrange
    const field = await client.$('~username');
    const visible = await field.isDisplayed();
    // Act
    await field.addValue('testUsername');
    // Assert
    expect(visible).toBeTruthy();
    expect(await field.getText()).toEqual('testUsername');
  });
});

That may be a lot of new code to digest at once, so let’s go line by line:

import wdio from 'webdriverio';  

First, we import the WebdriverIO client. This is the main package that will include the functionality we need to query elements from the react app and simulate events on the emulator.

jasmine.DEFAULT_TIMEOUT_INTERVAL = 60000;  

This will tell our test runner (in this case jest) to make the tests error after a certain number of ms  have passed. Here we’re setting it explicitly in the test, but if you’re using jest you can modify the testTimeout property on your jest configuration. If you’re using any other test runner,  I’d recommend going through their documentation, most of them have a similar property.

const opts = {  
  path: '/wd/hub/',
  port: 4723,
  capabilities: {
    platformName: 'android',
    deviceName: 'emulator-5554',
    app: 'my-app-name.apk',
    automationName: 'UiAutomator2',
  },
};

These are the configurations for our driver to know what to look for when using the appium interface to query and save elements. You can read more about them here.

You can get the device name going on your emulator > help > about

In order to generate an app from expo, you have to run the command:

expo build:android  

And wait in the queue for it to build.

In this case, I placed the downloaded apk in the root folder for my project, and renamed it my-app-name.apk.

Since we’re using WebdriverIO, the automationName will be UiAutomator2, as that’s how appium recognizes it.

Since lines 18-33 are  mostly about setup, we won’t focus on that for now. The next part focuses on line 34 and forward.

Writing the actual test

The idea of this test is just to showcase a normal flow on a test, therefore we will be dealing with a fairly simple use case: Checking that we have a valid username input:

const field = await client.$('~username');  
const visible = await field.isDisplayed();  

The first line allows us to query an item by accesibilityLabel.  As I have previously mentioned, for more information about specific selectors go to the appium documentation and the WebdriverIO documentation.

The second line checks whether our previously selected item is visible on the current screen, more information here.

await field.addValue('testUsername');  

This line simulates user typing into the selected field.  In this case, we’re inserting the ‘testUsername’ text inside the previously selected username field:

expect(visible).toBeTruthy();  
expect(await field.getText()).toEqual('testUsername');  

Lastly, we use Jest to check that the field is indeed visible on our Login Screen, and that the text on the given username field is the same as the one we wrote in it.

Running the test

Since we’re using Jest as our test runner on our react native app, I’ve set up a command on my package.json to run  the appium server and to run Jest in watch mode at the same time. It looks like this:

Here we’re using concurrently, a simple npm package that allows us to run several npm scripts at the same time. In this case we run the appium server and jest in watch mode, add their names and different colors to easily recognize them in the console, and pass the standard input to the jest command. This way we can narrow down our tests or do things like run coverage reports.

With this done, we simply have to run npm run test:e2e on our console, and expect something like this:

to be run, and something like this:

to be the output. If so, congratulations, you’ve correctly set up your integration tests for your react native app.

Wrapping up

While we’re far away from calling it a day on our e2e react app testing solution, the main automation testing setup it’s done. Next steps include integrating it with a CI/CD pipeline and making it work on IOS platforms.

Further Reading

Avatar Img
Pablo MarcanoFull Stack Developer @ Kaizen Softworks

Newest Posts

Meet our office in Salt Lake City
Posted 27 October, 2022

Meet our office in Salt Lake City

We've been working in offices for a long time. And we've learned that there's no one right way to do it. That's why when we opened our new commercial office in Salt Lake City, we did it differently the traditional way. We work in an open space, yes—but we also have private offices if we need one. Our office is part of a global network of workspaces that gives all the benefits of an open office without sacrificing privacy or productivity. During our stay in SLC from September 24th to October

#Announcements Business
Silicon Slopes Summit: a World-Class Business & Tech Event
Posted 20 October, 2022

Silicon Slopes Summit: a World-Class Business & Tech Event

Silicon Slopes Summit is one of the largest and most recognized business and technology events in the world. As every year, the sixth edition took place in Salt Lake City, Utah, being celebrated for the first time at the Vivint Arena.

#Events

Book Us

You call it a challenge? We are ready, bring it on