Running React Native Detox tests for iOS and Android on Github Actions
Tags: detox, reactnative, githubactions
Published: 2020-01-24 20:39:00

---
title: Running React Native Detox tests for iOS and Android on Github Actions
published: true
description: How to set up the workflows for your end to end mobile app testing
tags: Detox, React Native, Github Actions
---

If you are developing iOS and Android applications with React Native you might have encountered Detox. It's end-to-end testing and automation toolbox written and maintained by the Wix company. You can find some articles on dev.to about writing tests with Detox:
- React Native End to End Testing with Detox by Spencer Carli https://dev.to/spencercarli/react-native-end-to-end-testing-with-detox-56bk
- End-to-end testing in React Native with Detox
 by Brian Neville-O'Neill https://dev.to/bnevilleoneill/end-to-end-testing-in-react-native-with-detox-5736

When I start a new react-native project I want to have a test environment ready. You are more likely to test your code or your product if it's easy to write and run tests. I wanted to document how to setup Detox tests for React Native. Moreover, how to set up a workflow that on every push to Github I would get test runner within Github Actions.

## Starter Shell Repository

I have created a starter shell repository with the empty react-native project (with a single test) that has a Detox package installed.

https://github.com/edvinasbartkus/react-native-detox-github-actions

The project includes two workflows: iOS and Android. Both workflows are using macOS-latest environments to run the tests. Important to know, every second spent on MacOS machines is equal to 10 seconds on Linux. As of right now, Github provides 2000 minutes for free every month. That translates to 200 free minutes on MacOS machines.

I tried to get sdkmanager and adkmanager to Linux environment. However, that does not seem to be so easy. Emulators are not that easy to launch and run as you can imagine. If someone gets to set up Android emulators on Github CI Linux machines, I am more than welcome to get tips on how to do that.

## Steps
I go through all the steps to describe what's being done and why some options matter. The steps are based on iOS workflow: https://github.com/edvinasbartkus/react-native-detox-github-actions/blob/master/.github/workflows/ios.yml


- Simple name with a definition of hook - I want to run tests on every push.
```yaml
	name: iOS	 
	on: [push]
```

- Build environment: run tests on MacOS latest and if the tests don't finish in 15 minutes - kill it.
I had a few problems in my side projects where tests would hung up and would spend hours not doing anything just wasting valuable minutes.
```yaml
	jobs:
	  build:
	  runs-on: macos-latest
	  timeout-minutes: 15
```

- It's worth to have `DEVELOPER_DIR` defined pointing to an active installation of XCode. This changes with new versions of XCode. From time to time you have to go and look it up what's the directory for the latest version of XCode. Here is the page at Github Actions Help where all environment details are listed out: https://help.github.com/en/actions/automating-your-workflow-with-github-actions/virtual-environments-for-github-hosted-runners
```yaml 
	 env:
  	   DEVELOPER_DIR: /Applications/Xcode_11.2.app
```

- First actionable thing, let's get our source code of the project. We will not work with history so it's worth to checkout only the head of the repository.
```yaml
	 steps:
	   - name: Checkout
	     uses: actions/checkout@v1
	     with:
	       fetch-depth: 1
```

- We need Node to get npm packages installed.
```yaml
	 - name: Node
	   uses: actions/setup-node@v1
```

- I do cache node_modules/. While the project is young and empty, there is not much time saved with cached modules. As the project grows you will start to see bigger time wins because of caching.
```yaml
	 - name: Cache node modules
	   uses: actions/cache@v1
	   id: cache
	     with:
	     path: node_modules
	     key: node-modules-${{ hashFiles('**/yarn.lock') }}
```

- When we restore node modules from the cache, we need to rebuild the Detox Framework. Otherwise, we might get an error that the Detox Framework is missing when we want to run tests.
```yaml
	 - name: Rebuild detox
	   if: steps.cache.outputs.cache-hit == 'true'
	   run: detox clean-framework-cache && detox build-framework-cache
```

- In case if there is no cache we run regular `yarn install`.
```yaml 
	 - name: Install Dependencies
	   if: steps.cache.outputs.cache-hit != 'true'
	   run: yarn install
```


- Since we cache `node_modules/`, it's worth to cache pods directory. It's the next big thing that has to be installed every time we prepare the environment for testing. 
```yaml
	 - name: Cache Pods
	   uses: actions/cache@v1
	   id: podcache
	   with:
	     path: ios/Pods
	     key: pods-${{ hashFiles('**/Podfile.lock') }}
```

- This could be avoided since we restore pods from the cache but it's rather a fast operation if pods are already in place.
```yaml
	 - name: Update Pods
	   run: |
	     gem update cocoapods xcodeproj
	     cd ios && pod install && cd ..
```

- Finally, we install applesumutils (a requirement by detox) and we run tests.
```yaml
	 - run: brew tap wix/brew
	 - run: brew install applesimutils
	 - run: yarn detox build e2e --configuration ios.sim.release
	 - run: yarn detox test e2e --configuration ios.sim.release --cleanup --debug-synchronization 200
```

## Android Differences

In Android workflow, we follow the same sequence. There is only few steps that are specific to Android:
https://github.com/edvinasbartkus/react-native-detox-github-actions/blob/master/.github/workflows/android.yml

- We use an action to set up Java in MacOS environment.
```yaml
    - name: Use specific Java version for sdkmanager to work
      uses: joschi/setup-jdk@v1
      with:
        java-version: 'openjdk8'
        architecture: 'x64'
```

- Next, we download Android Emulator and we create an instance of emulator that is called `emu`
```yaml
    - name: Download Android Emulator Image
      run: |
        echo "y" | $ANDROID_HOME/tools/bin/sdkmanager --install "system-images;android-29;google_apis;x86"
        echo "no" | $ANDROID_HOME/tools/bin/avdmanager create avd --force --name emu --device "Nexus 5X" -k 'system-images;android-29;google_apis;x86'
        $ANDROID_HOME/emulator/emulator -list-avds
```

- We launch the emulator and wait until it is started
```yaml
    - name: Android Emulator
      timeout-minutes: 10
      continue-on-error: true
      run: |
        echo "Starting emulator"
        nohup $ANDROID_HOME/emulator/emulator -avd emu -no-audio -no-snapshot -no-window &
        $ANDROID_HOME/platform-tools/adb wait-for-device shell 'while [[ -z $(getprop sys.boot_completed | tr -d '\r') ]]; do sleep 1; done; input keyevent 82'
        $ANDROID_HOME/platform-tools/adb devices
        echo "Emulator started"
```

- Finally, we launch `webpack` server and run the tests
```yaml
    - name: Android Detox
      run: yarn start & detox test -c android.emu.debug
```

In packcage.json we have the following configuration for Android:

```yaml
  "detox": {
    "configurations": {
      "android.emu.debug": {
        "binaryPath": "android/app/build/outputs/apk/debug/app-debug.apk",
        "build":
        "cd android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug && cd ..",
        "type": "android.emulator",
        "name": "emu"
      }
    },
    "test-runner": "jest"
  }
```
Here we specify where our .apk file is placed and tell Detox what is the name of our emulator (it's `emu`).

Feel free to use the repo as a reference to set up the tests. If you see where it can be improved I would love to get pull requests and improve the workflows.