DEV Community

Cover image for End to End Testing with Detox on React-Native
Jeevan Kishore
Jeevan Kishore

Posted on • Edited on

End to End Testing with Detox on React-Native

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 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
  2. The demo source code is updated for the same in this repo

Why Detox?

What does detox offer over others? I'll let their own page talk about it


🔰 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,

   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
Enter fullscreen mode Exit fullscreen mode

If jest is not already installed in your project,

       npm install jest --save-dev
Enter fullscreen mode Exit fullscreen mode

Use this command for detox to generate a jest scaffolding,

      detox init -r jest
Enter fullscreen mode Exit fullscreen mode

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,

📃 package.json

  "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"
  }
Enter fullscreen mode Exit fullscreen mode

What's happening through the configuration? 😅

Let's walk through.

android

      "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"
        }
      }
Enter fullscreen mode Exit fullscreen mode

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 is a doc which details it.


ios

       "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"
        }
      }
Enter fullscreen mode Exit fullscreen mode

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

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


Setup detox with android:

📃 android/app/build.gradle

Add the following lines to default section

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

Add the following lines to dependencies section

       androidTestImplementation('com.wix:detox:+') { transitive = true }
       androidTestImplementation 'junit:junit:4.12'
Enter fullscreen mode Exit fullscreen mode

📃 android/app/src/androidTest/java/com/rndetox/DetoxTest.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);
       }
   }
Enter fullscreen mode Exit fullscreen mode

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

📃 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?)

    android:usesCleartextTraffic="true"
Enter fullscreen mode Exit fullscreen mode

📃 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,

    ext {
        buildToolsVersion = "28.0.3"
        minSdkVersion = 18
        compileSdkVersion = 28
        targetSdkVersion = 28
        kotlinVersion = "1.3.61"
    }
Enter fullscreen mode Exit fullscreen mode

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

    dependencies {
           // ...
           classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
       }
Enter fullscreen mode Exit fullscreen mode

Add the following maven snippet under repositories

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

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

⚠️ To verify if this are going good, cross check with this commit which shows the required file changes. ⚠️


🔰 Phase 2 - Write a test

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

📃 App.js

     <Text  testID="desc-text" style={styles.sectionDescription}>
Enter fullscreen mode Exit fullscreen mode

📃 e2e/firstTest.spec.js

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

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

   });
Enter fullscreen mode Exit fullscreen mode

🔰 Phase 3 - Build

Build a release

iOS:

    npx detox build -c ios.sim.release -l verbose
Enter fullscreen mode Exit fullscreen mode

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

Android:

    npx detox build -c android.emu.release -l verbose
Enter fullscreen mode Exit fullscreen mode

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

🔰 Phase 4 - Test

Test a release

iOS:

    npx detox test -c ios.sim.release -l verbose
Enter fullscreen mode Exit fullscreen mode

Android:

    npx detox test -c android.emu.release -l verbose
Enter fullscreen mode Exit fullscreen mode

Your tests should be passing with flying colors 🌈


🔰 Phase 5 - Setting up automation

Where is the fun without automating the entire workflow;

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

  • Integration with CircleCI
  • Integration with TravisCI

Check out the github repo for the entire codebase 🔥

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

Top comments (8)

Collapse
 
willybanus profile image
Willy Banús • Edited

Hi Jeevan thank you very much for this article.

i'm new in programming i was following detox documentation and your article but when i arrive to phase 3 to build a release fro Android i always get the same error. I've tried to find-out the solution before write you a comment and i would like to know if maybe you would know what's happening. Thanks in advanced.

// What i write
aklil@DESKTOP-0E8NOCM MINGW64 /d/e2e-detox(master)
$ detox build -c android.emu.release -l verbose
detox[3736] INFO: [build.js] cd android && ./gradlew app:assembleRelease assembleAndroidTest -DtestBuildType=release && cd ..

// The error
'.' is not recognized as an internal or external command,
operable program or batch file
detox[3736] ERROR: [cli.js] Error: Command failed: cd android && ./gradlew app:assembleRelease assembleAndroidTest -DtestBuildType=release && cd ..

After that Error i've forced reinstall npm install -g detox-cli and now it doesn't give the last error but when
i press enter after write the command:

detox build -c android.emu.release -l verbose

it keeps freeze, (it's a little advance) but it still doesn't work.

Collapse
 
fazlizekiqi profile image
Fazli Zekiqi

I am also having this problem although with iOS.
With the android however i am getting jest-runner error.
Did you find any good solution.

Collapse
 
jeevankishore profile image
Jeevan Kishore

@Fazli As i happen to mention to the comment above, I'm not entirely sure the cause of the issue in this case, i've updated the repo to accommodate the latest version of jest/detox, kindly refer the same and let me know if it works for you.

Thread Thread
 
fazlizekiqi profile image
Fazli Zekiqi

I am going to try it now.
On my iOS build however the build just freezes.
Here is my repo:
github.com/fazlizekiqi/mobileApp
I would really appreciate for some help.

Collapse
 
prasanta352 profile image
prasanta352

i was facing the same issue on windows
the issue was fixed by removing the '.' from " ./gradlew"

Collapse
 
jeevankishore profile image
Jeevan Kishore

@willy I'm not entirely sure the cause of the issue in this case, i've updated the repo to accommodate the latest version of jest/detox, kindly refer the same and let me know if it works for you.

Collapse
 
tsvetann profile image
Tsvetan Nikolov

Hi, thanks for the nice article. I also find myself deep in configuration when setting up detox so I created an npm package to automate detox setup. Hope it helps the community: react-native-setup-detox

Collapse
 
adithyaalladi profile image
adithyaalladi

Hi Jeevan,
thanks for your article. How can I get a test coverage report like for example an html report. Please advise.

Thanks