DEV Community

Edvinas Bartkus
Edvinas Bartkus

Posted on

Running React Native Detox tests for iOS and Android on 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:

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.
    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.
    jobs:
      build:
      runs-on: macos-latest
      timeout-minutes: 15
     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.
     steps:
       - name: Checkout
         uses: actions/checkout@v1
         with:
           fetch-depth: 1
  • We need Node to get npm packages installed.
     - 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.
     - 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.
     - 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.
     - 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.
     - 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.
     - 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.
     - 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.
    - 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
    - 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
    - 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
    - name: Android Detox
      run: yarn start & detox test -c android.emu.debug

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

  "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.

Top comments (3)

Collapse
 
fazlizekiqi profile image
Fazli Zekiqi

Great article. Currently i am working on a mono-repo with a team and we are using Travis CI as a build tool.

  • Do you have any recommendation on where to find good information about that? I 've tried the links but none of them has helped.
Collapse
 
martincernyawin profile image
Martin Cerny

Thank you for very useful article. I have tried to change build configuration from Release to Debug and it stopped working in Github Actions. Do you have any idea why is it not working?

Collapse
 
edvinasbartkus profile image
Edvinas Bartkus

@martin Cerny, sorry for the late response. Do you have any error message that you are seeing?