Blog

Automatic mobile app testing using Detox

At Ensolvers, we try to write as many automated test cases we can, in different layers of the software products we build, since it helps auto-documenting the code, ensure software quality and eases maintenance.

In our several React Native projects, we use Detox, a testing and automation library. Detox allows simulating user behaviour like taps, swipe, scroll, etc. in particular components and then assert for results – for instance, UI transformations. In this way, it is possible to replicate a full use-case flow in the application to test it from end to end.

Step1: InstallationInstalling detox in an existing project is pretty simple

1. Install Detox:

npm install detox --save-dev

2. Install a test runner, you can choose Jest or Mocha (in my case was Jest):

npm install jest --save-dev

3. Set up test-code scaffolds (automated)

detox init -r jest

this will create a e2e folder with config files and a first test.

4. Configuration
IOS
Add detox configuration to packaje.json:

"detox": {
  "configurations": {
    "ios.sim.debug": {
      "binaryPath": "ios/build/Build/Products/Debug-iphonesimulator/example.app",
      "build": "xcodebuild -project ios/example.xcodeproj -scheme example -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build",
      "type": "ios.simulator",
      "device": {
        "type": "iPhone 11 Pro"
      }
    }
  }
}

Android
a. Set sdk root adding following line to bashrc:

export ANDROID_SDK_ROOT=$HOME/Android/Sdk

b. Add Detox dependency to your root buildscript (i.e. android/build.gradle):

// Note: add the 'allproject' section if it doesn't exist
allprojects {
    repositories {
        // ...
        google()
        maven {
            // All of Detox' artifacts are provided via the npm module
            url "$rootDir/../node_modules/detox/Detox-android"
        }
    }
}

c. Add the following dependencies and defaultConfig to your app buildscript (i.e. app/build.gradle):

dependencies {
    // ...
       androidTestImplementation('com.wix:detox:+')
}

// …

android {
 // ...
 defaultConfig {
     // ...
     testBuildType System.getProperty('testBuildType', 'debug') 
     testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
 }
}

d. Add Kotlin to your classpath in the root build-script (i.e.android/build.gradle):

buildscript {
    ext {
        // …
        kotlinVersion = ‘1.3.71’
    }
    // ...
    dependencies {
        // ...
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
    }
}

e. Create Android Test class:
Add a file android/app/src/androidTest/java/com/[your.package]/DetoxTest.java and fill as in the detox example app for NR. Don’t forget to change the package name to your project’s (create folder androidTest if you need).

f. Add Detox configuration to package.json

"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",
            "device": {
              "avdName": <your_emulator_abd_name>
            }
        },
        "android.emu.release": {
            "binaryPath": "android/app/build/outputs/apk/release/app-release.apk",
            "build": "cd android && ./gradlew assembleRelease assembleAndroidTest -DtestBuildType=release && cd ..",
            "type": "android.emulator",
            "device": {
              "avdName": <your_emulator_abd_name>
            }
        }
    }
}

Note: You can get your list of available emulators with the following commands:

cd $ANDROID_SDK_ROOT/emulator
./emulator -list-avds

Step 3: Writing a basic test case.

Let’s assume we want to start a test interacting with a TouchableOpacity. First thing we need is to define a testID so we can reference the ponent via a Detox test:

<View>
  <TouchableOpacity testID='MyUniqueId123'>
    <Text>Some button</Text>
  </TouchableOpacity>
</View>

Note: since Detox forces us to define IDs for the componentes, it is considered a “gray-box” testing framework due to the fact that the code will reflect the fact that it is tested with Detox through this property.
Create a .spec.js file inside e2e folder (or use the existing file firstTest.spec.js) and paste the following code:

//Define a test block
describe('App Tests', () => {
 //Reset the App before each test
 beforeEach(async () => {
   await device.reloadReactNative();
 });


 //Define a test case
 it('test case description', async () => {
   //Your Code here, for example await element(by.id(‘id’)).tap()
 });


})

Match the element and perform an action:

await element(by.id('MyUniqueId123')).tap();

Set an expectation on the result:

await expect(element(by.id('AnotherUniqueId456'))).toBeVisible();


Step 4: Running the test.

Build the app:

detox build

Then, the tests can be run with the following commands
1. Android

detox test -c android.emu.debug

2. IOS

detox test -c ios.sim.debug

The command above will run the test (files with extension .spec.js) inside e2e folder.
That’s it. Your first failing Detox test is running!

Make more tests:

You can create more complex tests using the Detox API:
Matchers api
Actions api
Expectations api

Conclusion

Detox is a powerful library for testing your RN application in a simple and straightforward way taking three simple steps: match a component, perform an action and expect a result.

By following these 3 steps repeatedly we can generate the different flows of the application testing it end to end, in a really short time compared to the time it would take to do it manually.

Compartir el post