End-to-end testing in React Native with Detox

A quick introduction to end-to-end testing 📖

End-to-End Testing with Cypress

End-to-end testing in mobile 🤯

End-to-End Testing with Detox

What is Detox, and why should you pick it?

describe('Login flow', () => {
it('should login successfully', async () => {
await device.reloadReactNative();
// getting the reference of an element by ID and expecting to be visible
await expect(element(by.id('email'))).toBeVisible();

// Getting the reference and typing
await element(by.id('email')).typeText('john@example.com');
await element(by.id('password')).typeText('123456');

// Getting the reference and executing a tap/press
await element(by.text('Login')).tap();

await expect(element(by.text('Welcome'))).toBeVisible();
await expect(element(by.id('email'))).toNotExist();
});
});

Ready, set, code! 🏎

~ npm install react-native -g
~ react-native init testReactNativeDetox

###### ######
### #### #### ###
## ### ### ##
## #### ##
## #### ##
## ## ## ##
## ### ### ##
## ######################## ##
###### ### ### ######
### ## ## ## ## ###
### ## ### #### ### ## ###
## #### ######## #### ##
## ### ########## ### ##
## #### ######## #### ##
### ## ### #### ### ## ###
### ## ## ## ## ###
###### ### ### ######
## ######################## ##
## ### ### ##
## ## ## ##
## #### ##
## #### ##
## ### ### ##
### #### #### ###
###### ######


Welcome to React Native!
Learn Once Write Anywhere

✔ Downloading template
✔ Copying template
✔ Processing template
✔ Installing dependencies
✔ Installing CocoaPods dependencies (this may take a few minutes)

Run instructions for iOS:
• cd testReactNativeDetox && react-native run-ios
- or -
• Open testReactNativeDetox/ios/testReactNativeDetox.xcworkspace in Xcode or run "xed -b ios"
• Hit the Run button

Run instructions for Android:
• Have an Android emulator running (quickest way to get started), or a device connected.
• cd testReactNativeDetox && react-native run-android
~ cd testReactNativeDetox
~ react-native run-ios
Application running

Time to test! 🔧

~ yarn add detox -D
~  detox init -r jestdetox[34202] INFO:  [init.js] Created a file at path: e2e/config.json
detox[34202] INFO: [init.js] Created a file at path: e2e/init.js
detox[34202] INFO: [init.js] Created a file at path: e2e/firstTest.spec.js
detox[34202] INFO: [init.js] Patching package.json at path: /Users/USERNAME/Git/testReactNativeDetox/package.json
detox[34202] INFO: [init.js] json["detox"]["test-runner"] = "jest";
describe('Example', () => {
beforeEach(async () => {
await device.reloadReactNative();
});

it('should have "Step One" section', async () => {
await expect(element(by.text('Step One'))).toBeVisible();
});

it('should have "See Your Changes" section', async () => {
await expect(element(by.text('See Your Changes'))).toBeVisible();
});
});
{
"detox": {
"test-runner": "jest",
"configurations": {
"ios.release": {
"binaryPath": "./ios/build/Build/Products/Release-iphonesimulator/testReactNativeDetox.app",
"build": "xcodebuild -workspace ios/testReactNativeDetox.xcworkspace -configuration release -scheme testReactNativeDetox -sdk iphonesimulator -derivedDataPath ios/build",
"type": "ios.simulator",
"name": "iPhone X"
}
}
}
}
~ detox build
~ detox test

PASS e2e/firstTest.spec.js (7.514s)
Example
✓ should have "Step One" section (260ms)
✓ should have "See Your Changes" section (278ms)
Initial Detox setup

Time to make it fancier! 💅

~ yarn add react-swipeable-views-native randomcolor
Demo with carousel

Writing Detox tests 🧪

{
"scripts": {
"e2e:test": "detox test -c ios.release",
"e2e:build": "detox build -c ios.release"
}
}
~ yarn e2e:build
describe('Example', () => {
beforeEach(async () => {
await device.reloadReactNative();
});

it('should show "Step One"', async () => {
await expect(element(by.text('Step One'))).toBeVisible();
});

it('should show "See Your Changes"', async () => {
await expect(element(by.text('See Your Changes'))).toBeVisible(); // THIS TEST WILL FAIL!
});
});
describe('Example', () => {
// previous tests here

it('should render "See Your Changes" in the second slide', async () => {
// getting the reference of the slides and make a swipe
await element(by.id('slides')).swipe('left');
await expect(element(by.text('See Your Changes'))).toBeVisible(); // no this will pass!
});
});
~ yarn e2e:test

PASS e2e/firstTest.spec.js (7.514s)
Example
✓ should have "Step One" section (260ms)
✓ should render "See Your Changes" in the second slide (993ms)
Running a basic test
describe('Example', () => {
// previous tests here
it('should enable swiping back and forth', async () => {
await expect(element(by.text('Step One'))).toBeVisible();
await element(by.id('slides')).swipe('left');
await element(by.id('slides')).swipe('right');
await expect(element(by.text('Step One'))).toBeVisible();
});
it('should render "Debug" and have a Button to click in the third slide', async () => {
await element(by.id('slides')).swipe('left');
await element(by.id('slides')).swipe('left');
await expect(element(by.text('Debug'))).toBeVisible();
await element(by.text('Click here!')).tap();
await expect(element(by.text('Clicked!'))).toBeVisible();
});
it('should render "Learn More" and change text in the fourth slide', async () => {
await element(by.id('slides')).swipe('left');
await element(by.id('slides')).swipe('left');
await element(by.id('slides')).swipe('left');
await expect(element(by.text('Learn More'))).toBeVisible();
const docsInput = element(by.id('docsInput')); await expect(docsInput).toBeVisible(); await docsInput.clearText();
await docsInput.typeText('Maybe later!');
await expect(docsInput).toHaveText('Maybe later!');
});
});
~ yarn e2e:test PASS  e2e/firstTest.spec.js (22.128s)
Example
✓ should have "Step One" section (268ms)
✓ should render "See Your Changes" in the second slide (982ms)
✓ should enable swiping back and forth (1861ms)
✓ should render "Debug" and have a Button to click in the third slide (2710ms)
✓ should render "Learn More" and change text in the fourth slide (9964ms)
Running all tests

Bonus: Running E2E test in CI 🎁

language: objective-c
osx_image: xcode10.2
branches:
only:
- master
env:
global:
- NODE_VERSION=stable
install:
- brew tap wix/brew
- brew install applesimutils
- curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.2/install.sh | bash
- export NVM_DIR="$HOME/.nvm" && [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
- nvm install $NODE_VERSION
- nvm use $NODE_VERSION
- nvm alias default $NODE_VERSION
- npm install -g react-native-cli
- npm install -g detox-cli
- npm install
- cd ios; pod install; cd -;
script:
- npm run e2e:ci
{
"scripts": {
"e2e:test": "detox test -c ios.release",
"e2e:build": "detox build -c ios.release",
"e2e:ci": "npm run e2e:build && npm run e2e:test -- --cleanup"
}
}
Travis PR Checker

Closing words

References and further reading

Software Engineer during the day, cook at night 👨‍🍳 Traveller and Foodie 🧳 Whenever I can I like to write and speak 🤓 Berlin 📍

Get the Medium app