End to End Testing with Detox on React-Native
Tags: reactnative, automation, javascript, testing
Published: 2020-05-03 19:33:16

---
title: End to End Testing with Detox on React-Native
published: true
description: Automating gray box end-to-end testing with detox
tags: #reactnative, #automation, #javascript, #testing
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/60xcwm5yn7xqccqbwt86.jpg
---

### Assumptions

Before we begin, this article assumes RN (expo or otherwise) is set up on your terminal and your app is up and running.

If not please see [how](https://reactnative.dev/docs/environment-setup) to do that.

We will be proceeding with a setup built using React Native CLI. 

Please ensure the builds are working before integration to make it easier later on to debug if need be.

There might be requirements such as specific ndk versions to be installed

P.S Either will do.

Our app is called `rndetox` (it's weird. I know.)


> There have been breaking changes implemented with detox v18, 
1. [Migration](https://github.com/wix/Detox/blob/master/docs/Guide.Migration.md)
2. The demo source code is updated for the same in [this repo](https://github.com/Jeevan-Kishore/e2e-detox-18) 


### Why _Detox_?

What does detox offer over others? I'll let their own [page](https://github.com/wix/Detox#about) talk about it 

---

### :beginner: Phase 1 - Setting up detox

By this time, your RN app should be up and running on a mac machine. Let's proceed setting up Detox.

#### Installing packages:

Install the following packages using your `terminal`,

```sh
   xcode-select --install
   brew update && brew install node
   brew tap wix/brew
   brew install applesimutils
   npm install -g detox-cli
   npm install detox --save-dev
```


If jest is not already installed in your project,
```sh
       npm install jest --save-dev
```


Use this command for detox to generate a jest scaffolding,
```sh
      detox init -r jest
```

It will create a bunch of files under `e2e` directory with preset configurations.


#### Add detox configuration:

The following configuration needs to be added in `package.json` of the project,

:page_with_curl: _package.json_

```javascript
  "detox": {
    "configurations": {
      "android.emu.debug": {
        "binaryPath": "android/app/build/outputs/apk/debug/app-debug.apk",
        "build": "cd android && ./gradlew app:assembleDebug assembleAndroidTest -DtestBuildType=debug && cd ..",
        "type": "android.emulator",
        "device": {
          "avdName": "detoxTestEmulator"
        }
      },
      "android.emu.release": {
        "binaryPath": "android/app/build/outputs/apk/release/app-release.apk",
        "build": "cd android && ./gradlew app:assembleRelease assembleAndroidTest -DtestBuildType=release && cd ..",
        "type": "android.emulator",
        "device": {
          "avdName": "detoxTestEmulator"
        }
      },
      "ios.sim.release": {
        "binaryPath": "ios/build/Build/Products/Release-iphonesimulator/rndetox",
        "build": "export RCT_NO_LAUNCH_PACKAGER=true && xcodebuild -workspace ios/a.xcworkspace -scheme a -configuration Release -sdk iphonesimulator -derivedDataPath ios/build",
        "type": "ios.simulator",
        "device": {
          "type": "iPhone 11 Pro"
        }
      },
      "ios.sim.debug": {
        "binaryPath": "ios/build/Build/Products/Debug-iphonesimulator/rndetox",
        "build": "xcodebuild -workspace ios/a.xcworkspace  -scheme a -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build",
        "type": "ios.simulator",
        "device": {
          "type": "iPhone 11 Pro"
        }
      }
    },
    "test-runner": "jest"
  }
```

#### What's happening through the configuration? :sweat_smile:

Let's walk through.


_android_
```javascript
      "android.emu.release": {
        "binaryPath": "android/app/build/outputs/apk/release/app-release.apk",
        "build": "cd android && ./gradlew app:assembleRelease assembleAndroidTest -DtestBuildType=release && cd ..",
        "type": "android.emulator",
        "device": {
          "avdName": "detoxTestEmulator"
        }
      }
```

The android release configuration consists of :
- holds path to the built apk to test
- build commands with build type
- type and name of the emulator (this name should be the same as the emulator which should be created using android studio)

Note: On how to create emulators, [here](https://developer.android.com/studio/run/managing-avds) is a doc which details it.

---

_ios_
```javascript
       "ios.sim.release": {
        "binaryPath": "ios/build/Build/Products/Release-iphonesimulator/rndetox",
        "build": "export RCT_NO_LAUNCH_PACKAGER=true && xcodebuild -workspace ios/a.xcworkspace -scheme a -configuration Release -sdk iphonesimulator -derivedDataPath ios/build",
        "type": "ios.simulator",
        "device": {
          "type": "iPhone 11 Pro"
        }
      }
```

The android release configuration consists of :
- path to built binary (`rndetox` is the name of our app hence it's `rndetox`, it would be [app_name].app)
- build commands with scheme and path to workspace as per the name of the app (ios/[app_name].xcworkspace)
- type and device the tests are to run on

With the detox configuration in place, iOS is all set to be worked with. :tada:

Android on the other hand, well.. " Needs a lot more work. " :snowman:

---

#### Setup detox with android:


:page_with_curl: _android/app/build.gradle_

Add the following lines to `default` section 

```gradle
       testBuildType System.getProperty('testBuildType', 'debug')  // This will later be used to control the test apk build type for detox
       testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
```

Add the following lines to `dependencies` section

```gradle
       androidTestImplementation('com.wix:detox:+') { transitive = true }
       androidTestImplementation 'junit:junit:4.12'
```



:page_with_curl: _android/app/src/androidTest/java/com/rndetox/DetoxTest.java_

```java
   package com.rndetox; /* change to app package name */

   import com.wix.detox.Detox;

   import org.junit.Rule;
   import org.junit.Test;
   import org.junit.runner.RunWith;

   import androidx.test.ext.junit.runners.AndroidJUnit4;
   import androidx.test.filters.LargeTest;
   import androidx.test.rule.ActivityTestRule;

   @RunWith(AndroidJUnit4.class)
   @LargeTest
   public class DetoxTest {

       @Rule
       public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(MainActivity.class, false, false);

       @Test
       public void runDetoxTests() {
           Detox.runTests(mActivityRule);
       }
   }
```

The above file should be created as per the hierarchical order `android/app/src/androidTest/java/com/[app_name]/DetoxTest.java`

:page_with_curl: _android/app/src/main/AndroidManifest.xml_

The snippet should be added to the `<application` tag, this is required for detox to perform as expected ([what is this?](https://developer.android.com/guide/topics/manifest/application-element))
```xml
    android:usesCleartextTraffic="true"
```


:page_with_curl: _android/build.gradle_

Change `minSdkVersion` to `18` or higher, add `kotlinVersion` if it's not already present under `ext`.

It should look similar to this,

```gradle
    ext {
        buildToolsVersion = "28.0.3"
        minSdkVersion = 18
        compileSdkVersion = 28
        targetSdkVersion = 28
        kotlinVersion = "1.3.61"
    }
```

Add `classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"` under dependencies

```gradle
    dependencies {
           // ...
           classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
       }
```


Add the following `maven` snippet under `repositories`

```gradle
   allprojects {
       repositories {
                // ... statements
           maven {
               // All of Detox' artifacts are provided via the npm module
               url "$rootDir/../node_modules/detox/Detox-android"
           }
       }
   }
```


With the above steps done, android is all set to go. :tada:


:warning: To verify if this are going good, cross check with this [commit](https://github.com/Jeevan-Kishore/e2e-detox/commit/8eb5b1ef469b7236d880717d9f439b802a6f1ade) which shows the required file changes. :warning:

---

### :beginner: Phase 2 - Write a test 

To see if things fall in place, let's add a test id and assert it.

:page_with_curl: _App.js_

```javascript
     <Text  testID="desc-text" style={styles.sectionDescription}>
```

:page_with_curl: _e2e/firstTest.spec.js_

```javascript
   describe('Example', () => {
     beforeEach(async () => {
       await device.reloadReactNative();
     });

     it('should have description text on welcome screen', async () => {
       await expect(element(by.id('desc-text'))).toBeVisible();
     });

   });
```

---

### :beginner: Phase 3 - Build

#### Build a release

_iOS:_
```sh
    npx detox build -c ios.sim.release -l verbose
```

If there are build errors, build on xcode to get details on the same

_Android:_
```sh
    npx detox build -c android.emu.release -l verbose
```
If there are build errors, build on android studio to get details on the same
---

### :beginner: Phase 4 - Test

#### Test a release

_iOS:_
```sh
    npx detox test -c ios.sim.release -l verbose
```

_Android:_ 
```sh
    npx detox test -c android.emu.release -l verbose
```

Your tests should be passing with flying colors :rainbow:

---

### :beginner: Phase 5 - Setting up automation

Where is the fun without automating the entire workflow;

It's an :elephant: on it's own, will try to address them individually.

- Integration with CircleCI
- Integration with TravisCI

Check out the github [repo](https://github.com/Jeevan-Kishore/e2e-detox-18) for the entire codebase 🔥

If you have questions, let us know in the comments and we are looking forward for your feedback 🍻