, ,

Android SDK and emulator in Docker for testing

Michael Laemmle

Related articles: ►Take Me Home – Project Overview  ►CI/CD infrastructure: Choosing and setting up a server with Jenkins as Docker image Automated Unit- and GUI-Testing for Android in Jenkins  ►Testing a MongoDB with NodeJS, Mocha and Mongoose


During our Android development project, we had to cope with several technological and organizational challenges with regard to construct a stable CI pipeline. Due to our chosen stack of Docker containers for building and deploying we had to confront with the topic how to integrate and deploy a building and testing environment for Android in Docker.

For a better understanding of this challenge following illustration of our stack can be consulted:



We recognize that our docker environment is built up in three containers. The “build”-Container with a Jenkins environment at the bottom. Our “database”-Container in the center and our “SDK and Emulator”-container on top.

Following challenges have emerged:

  1. How to set-up a build environment for Jenkins in which the Android SDK dependency is located in a different Docker container?
  2. How to deploy the build artifacts on this emulator and run UI-Tests

For a better understanding of our first challenge we must understand how we run an Android build in Jenkins.

We used a standard pipeline project for this task which is separated in four Stages:

Each stage is responsible for an automated step in the build process. In case of an Android build several stages are gradle commands. This means that especially stage two and three are essential.
Step two builds a runnable Android artefact, respectively an .APK file. Step three executes the Android project’s unit tests. To run these gradle commands in a pipeline script you can use a gradle wrapper. A gradle wrapper encapsulates a specific gradle version in the project or installs a specific version if needed. Or You can specify and install gradle through the available Gradle Plugin for Jenkins:
See  https://wiki.jenkins.io/display/JENKINS/Gradle+Plugin.

Another thing is the configuration of the stages is the pipeline script. To invoke gradle commands you should wrap gradle in a separate command like:

def gradle(command) {
    sh "${tool name: 'gradle', type: 'hudson.plugins.gradle.GradleInstallation'}/bin/gradle ${command}"
}

By declaring this function, you can start any gradle operation by passing the gradle command in the parameter.

E.g.

gradle 'clean test'

or

gradle 'assembleDebug'

As said at the beginning, gradle uses the Android SDK to build and tests the project. To find the Android SDK gradle resolves it by default in the environment variable ANDROID_HOME.
But what do you have to do if the path of ANDROID_HOME is located in another Docker container?

We solved this problem by declaring a Volume for the Android SDK by providing it for our Jenkins container. If you use Docker compose you can easily declare a volume from one container to expose it for another. By doing this you can create a shared path between two or more containers. This could look like this:

version: '3'
services:
  jenkins:
    image: "dockerid/take-me-home:1.6"
    container_name: "jenkins"
    ports:
      - "80:8080"
    volumes:
      - app-volume:/usr/local/android-sdk
      - ./jenkins_mount_point:/var/jenkins_home
  mongodb:
    image: mongo:latest
    container_name: "mongodb"
    environment:
      - MONGO_DATA_DIR=/data/db
      - MONGO_LOG_DIR=/dev/null
    volumes:
      - ./data/db:/data/db
    ports:
      - 27017:27017

  android-emulator:
    image: "butomo1989/docker-android-x86-7.1.1"
    container_name: "android-emulator"
    volumes:
        - app-volume:/root/
    ports:
       - "6080:6080"
       - "5554:5554"
       - "5555:5555"
    args:
            DEVICE: "Nexus 5"

### share volume
volumes:
    app-volume:

As you can see a volume called “app-volume” is created. In the android-emulator image the SDK is located in /root/ and will be mapped to /usr/local/android-sdk in the Jenkins image. Now you only must make sure that your android emulator image is up and running and you have to set the ANDROID_HOME variable to this location.

Now that we can build our Android App with the SDK from the emulator container we can put our focus on UI-Tests and deploying on a Docker-driven Emulator:

If you search in Docker Hub for Android Emulator you will find a couple results. Our experience was that most of these Docker images were outdated or not maintained anymore. The problem is that often after updating the SDK via SDKMANAGER the requirements to the environments are changing. This means that commands in the startup script are often deprecated, so you should build an image by yourself or search a recent image that is not older than a couple of weeks. In our case the image docker-android-x86-7.1.1 from butomo1989 was well matching. You can find his Docker repo here.

Butomo1989 did a great job creating this image. He provides some nice features in his build.
For example:

  • noVNC Support (You can connect via Browser to a running emulator)
  • You can run UI Tests with different 3-rd party frameworks (like Appium, Espresso)
  • And many more

Google provides its own UI Testing framework. It is called the Espresso framework. The tests are written in JUnit4 Style. An example can be seen here.

In our fifth Stage which is called “Test on emulator” we call our emulator by adb. Make sure you have the Android Debug Bridge installed on your Host machine. In our case the adb must be installed in the Jenkins-Docker image.

You can connect to the emulator device just by calling it in the pipeline script:

sh adb connect <docker-machine-ip-address>:5555

After a successful connection the adb test command can be called:

sh adb shell am instrument -w <test_package_name>/<runner_class>

Providing your Android SDK and Emulator in a separated Docker container gives you certain possibilities. First, it is separated from your building environment. So, you can update your Jenkins container and you don’t have to look after you Android SDK. Second, encapsulating the emulator in another image enables you for resizing and adding more images to your emulator. You could run the container on another Docker Host to distribute load due to high performance requirements of the emulator. If you have got further opinions on this topic do not hesitate to contact me:
ml126@hdm-stuttgart.de.


by

Michael Laemmle

Tags:

Comments

Leave a Reply