React Native Integration Testing Using Appium & Jest — Teamweek Blog

At Teamweek, we test our mobile applications using Webdriver, Appium and Jest. The excellent npm module, wd (a Webdriver library for Node.js) simplifies writing automated tests that can be run on a simulator (or actual device) via an Appium daemon. Appium in this case uses platform specific automation frameworks to simulate the actual user interactions.

This short guide will be a gentle introduction to testing React Native apps using these tools.

  1. xcode or Android Studio, depending on the target platform
  2. appium, jest and wd npm modules (npm install appium wd jest –save-dev)

Lets assume that we have the following component that defines an image that gets replaced with a message when tapped:

class Component extends React.Component {
state = {
show: false
}
render() {
return this.state.show
? (<Text>Hello World!</Text>)
: (<TouchableOpacity
onPress={() => this.setState({show: true})>
<Image
source='images/greet.png'
accessibilityLabel='Greet' />
</TouchableOpacity>);
}
}

We’ll be writing a small test case for the described user action:

class Helper {
setup() {}
teardown() {}
}
const helper = new Helper();
describe('--> Greet Button <--', () => {
beforeEach(() => helper.setup());
afterEach(() => helper.teardown());
test('shows greeting when tapped', () => {});
});

Our test suite will use a Helper class that will hold common code that will be called before (setup) and after (teardown) each test (if we ever add more). The setup hook will specifically create an instance of the wd driver that targets the iOS platform as shown below:

import wd from 'wd';
class Helper {
setup() {
const capabilities = {
platformName: 'iOS',
deviceName: 'iPhone 6',
platformVersion: '10.2',
app: 'ios/build/Build/Products/Debug-iphonesimulator/TW.app',
};
this.driver = wd.promiseChainRemote('0.0.0.0', 4723);
return this.driver.init(capabilities);
}
...
}

On the other hand, the teardown would reset the simulator.

class Helper {
...
teardown() {
return this.driver.quit();
}
}

Note that both the setup and teardown hooks return promises that get fulfilled when the connection to the simulator is ready and has been successfully shutdown respectively.

Now that the helper is ready, we implement the logic of our single test case:

test('shows greeting when tapped', () => {
return helpers
.driver
.elementByAccessibilityId('Greet')
.tap()
.waitForElementById('Hello World!');
});

The web driver instance provides several useful element selector methods and other utilities that simplify writing the integration tests. In the test case above, we get an reference to the image, tap it and expect it to be replaced by a greeting.

At Teamweek, we have found it reliable to use the elementById and elementByAccessibilityId selectors when writing iOS specific tests and elementByXPath in the case of Android. Therefore on android, we would have something like:

driver
.elementByXPath(
"//android.widget.ImageView[@content-desc=\'Greet\']"
)
.tap()
.waitForElementById(
"//android.widget.TextView[@text=\'Hello World!\']"
);

Running the Test

Before running the tests, we would need to run the appium server:

$ appium

Followed by the React Native Builder (in a new terminal tab):

$ react-native run-ios --simulator='iPhone 6'

Any test runner other than Jest can be used in conjunction with Appium and WD. In our case, we would need to run the tests using the –runInBand or -i flag as this would force the tests to be run serially unless one plan’s to launch multiple device simulators to execute multiple tests in parallel.

Conclusion

This post hopefully raised a little curiosity in trying the mentioned technologies in your stack and so feedback would be highly appreciated in the comments section below.


Originally published at blog.teamweek.com on July 18, 2017.