How to run Detox tests on GitHub Actions

Detox logo
Detox

This post goes over how to set up and run React Native Detox tests on GitHub Actions.

Prerequisites

You have a React Native project with Detox set up.

iOS

Create a GitHub Actions workflow named e2e-ios.yml:

mkdir -p .github/workflows && touch .github/workflows/e2e-ios.yml

Name your workflow, set up the event that triggers the workflow, and create a job that runs on macOS:

name: e2e-ios
on: push
jobs:
e2e-ios:
runs-on: macos-latest
steps:
# ...

iOS Steps

Check out the repository:

- name: Checkout repository
uses: actions/checkout@v3

Set up Node.js:

- name: Setup Node.js
uses: actions/setup-node@v3
with:
cache: yarn
node-version-file: .node-version

If you’re using NVM, replace .node-version with .nvmrc.

Install node_modules with Yarn:

- name: Install Yarn dependencies
run: yarn --frozen-lockfile --prefer-offline

Install applesimutils with Homebrew:

- name: Install macOS dependencies
run: |
brew tap wix/brew
brew install applesimutils

If you want to speed up the step, you can disable Homebrew’s auto update and install cleanup:

env:
HOMEBREW_NO_AUTO_UPDATE: 1
HOMEBREW_NO_INSTALL_CLEANUP: 1

Set up Ruby, run bundle install, and cache gems:

- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
bundler-cache: true

ruby/setup-ruby will get the Ruby version from .ruby-version.

Install and cache CocoaPods:

- name: Cache CocoaPods
id: cache-cocoapods
uses: actions/cache@v3
with:
path: ios/Pods
key: ${{ runner.os }}-pods-${{ hashFiles('ios/Podfile.lock') }}
restore-keys: |
${{ runner.os }}-pods-

- name: Install CocoaPods
if: steps.cache-cocoapods.outputs.cache-hit != 'true'
run: cd ios ; pod install ; cd -

Optionally, you can clean and build Detox framework cache:

- name: Detox rebuild framework cache
run: yarn detox rebuild-framework-cache

Build and cache Detox iOS (release) build:

- name: Cache Detox build
id: cache-detox-build
uses: actions/cache@v3
with:
path: ios/build
key: ${{ runner.os }}-detox-build
restore-keys: |
${{ runner.os }}-detox-build

- name: Detox build
run: yarn detox build --configuration ios.sim.release

This builds the iOS app using the ios.sim.release configuration in .detoxrc.js.

Run Detox tests:

- name: Detox test
run: yarn detox test --configuration ios.sim.release --cleanup --headless --record-logs all

If you get the error Exceeded timeout of 120000ms while setting up Detox environment, then increase the testRunner.jest.setupTimeout in .detoxrc.js.

If there’s a failure, upload the Detox artifacts so you can download them after the workflow ends:

- name: Upload artifacts
if: failure()
uses: actions/upload-artifact@v3
with:
name: detox-artifacts
path: artifacts

iOS Workflow

Here’s the full E2E workflow for iOS (see code):

# .github/workflows/e2e-ios.yml
name: e2e-ios
on: push

jobs:
e2e-ios:
runs-on: macos-latest

steps:
- name: Checkout repository
uses: actions/checkout@v3

- name: Setup Node.js
uses: actions/setup-node@v3
with:
cache: yarn
node-version-file: .node-version

- name: Install Yarn dependencies
run: yarn --frozen-lockfile --prefer-offline

- name: Install macOS dependencies
run: |
brew tap wix/brew
brew install applesimutils
env:
HOMEBREW_NO_AUTO_UPDATE: 1
HOMEBREW_NO_INSTALL_CLEANUP: 1

- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
bundler-cache: true

- name: Cache CocoaPods
id: cache-cocoapods
uses: actions/cache@v3
with:
path: ios/Pods
key: ${{ runner.os }}-pods-${{ hashFiles('ios/Podfile.lock') }}
restore-keys: |
${{ runner.os }}-pods-

- name: Install CocoaPods
if: steps.cache-cocoapods.outputs.cache-hit != 'true'
run: cd ios ; pod install ; cd -

- name: Detox rebuild framework cache
run: yarn detox rebuild-framework-cache

- name: Cache Detox build
id: cache-detox-build
uses: actions/cache@v3
with:
path: ios/build
key: ${{ runner.os }}-detox-build
restore-keys: |
${{ runner.os }}-detox-build

- name: Detox build
run: yarn detox build --configuration ios.sim.release

- name: Detox test
run: yarn detox test --configuration ios.sim.release --cleanup --headless --record-logs all

- name: Upload artifacts
if: failure()
uses: actions/upload-artifact@v3
with:
name: detox-artifacts
path: artifacts

Android

Before you start, make sure you first patch your project with the additional Android configuration.

Create a GitHub Actions workflow named e2e-android.yml:

mkdir -p .github/workflows && touch .github/workflows/e2e-android.yml

Name your workflow, set up the event that triggers the workflow, and create a job that runs on macOS:

name: e2e-android
on: push
jobs:
e2e-android:
runs-on: macos-latest
steps:
# ...

Android Steps

Check out the repository:

- name: Checkout repository
uses: actions/checkout@v3

Set up Node.js:

- name: Setup Node.js
uses: actions/setup-node@v3
with:
cache: yarn
node-version-file: .node-version # .nvmrc

Install node_modules with Yarn:

- name: Install Yarn dependencies
run: yarn --frozen-lockfile --prefer-offline

Set up Java:

- name: Setup Java
uses: actions/setup-java@v3
with:
cache: gradle
distribution: temurin
java-version: 17

Build and cache Detox Android (release) build:

- name: Cache Detox build
id: cache-detox-build
uses: actions/cache@v3
with:
path: ios/build
key: ${{ runner.os }}-detox-build
restore-keys: |
${{ runner.os }}-detox-build

- name: Detox build
run: yarn detox build --configuration android.emu.release

This builds the Android app using the android.emu.release configuration in .detoxrc.js.

Get the Android Virtual Device (AVD) name from .detoxrc.js:

- name: Get device name
id: device
run: node -e "console.log('AVD_NAME=' + require('./.detoxrc').devices.emulator.device.avdName)" >> $GITHUB_OUTPUT

Run Detox tests using reactivecircus/android-emulator-runner:

- name: Detox test
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: 31
arch: x86_64
avd-name: ${{ steps.device.outputs.AVD_NAME }}
script: yarn detox test --configuration android.emu.release --headless --record-logs all

Make sure not to pass the --cleanup option in the script or else it will throw an error:

detox[27246] i adb: error: device 'emulator-5554' not found

If there’s a failure, upload the Detox artifacts so you can download them after the workflow ends:

- name: Upload artifacts
if: failure()
uses: actions/upload-artifact@v3
with:
name: detox-artifacts
path: artifacts

Android Workflow

Here’s the full E2E workflow for Android (see code):

# .github/workflows/e2e-android.yml
name: e2e-android
on: push

jobs:
e2e-android:
runs-on: macos-latest

steps:
- name: Checkout repository
uses: actions/checkout@v3

- name: Setup Node.js
uses: actions/setup-node@v3
with:
cache: yarn
node-version-file: .node-version

- name: Install Yarn dependencies
run: yarn --frozen-lockfile --prefer-offline

- name: Setup Java
uses: actions/setup-java@v3
with:
cache: gradle
distribution: temurin
java-version: 17

- name: Cache Detox build
id: cache-detox-build
uses: actions/cache@v3
with:
path: android/app/build
key: ${{ runner.os }}-detox-build
restore-keys: |
${{ runner.os }}-detox-build

- name: Detox build
run: yarn detox build --configuration android.emu.release

- name: Get device name
id: device
run: node -e "console.log('AVD_NAME=' + require('./.detoxrc').devices.emulator.device.avdName)" >> $GITHUB_OUTPUT

- name: Detox test
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: 31
arch: x86_64
avd-name: ${{ steps.device.outputs.AVD_NAME }}
script: yarn detox test --configuration android.emu.release --headless --record-logs all

- name: Upload artifacts
if: failure()
uses: actions/upload-artifact@v3
with:
name: detox-artifacts
path: artifacts

--

--

remarkablemark.org

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store