Automated UI tests on Android
January 17, 2017 [Android, Rabbit Escape, Tech]I recently fought the Android emulator a lot to get my UI tests to run automatically during the build of Rabbit Escape, so I thought I'd better write down what I did before I forget.
I already have tests that drive the Android UI (see e.g. SmokeTest.java - it's not pretty, but it seems reliable, and it catches real problems) - this post is about how to run them automatically during our build.
Note that to run the Android emulator I'm fairly sure you need a windowing environment, so I don't think this could be moved to a headless build server. If course, you could always fight some kind of framebuffer thing.
Here is the part of our Makefile that launches the tests:
android-smoke-tests: @echo ". Running Android smoke tests" ./build-scripts/android-start-emulator "android-8" "3.2in QVGA (ADP2)" ./build-scripts/android-test "free" "app-free-debug" ./build-scripts/android-test "" "app-paid-debug" ./build-scripts/android-stop-emulator
and here is ./build-scripts/android-start-emulator - it starts up and emulator, waits for it to be ready, and unlocks its screen.:
#!/bin/bash
set -x
set -u
set -e
# Args
TARGET="$1" # E.g. "android-8"
DEVICE="$2" # E.g. "3.2in QVGA (ADP2)"
# Setup
ADB="${HOME}/Android/Sdk/platform-tools/adb"
EMULATOR="${HOME}/Android/Sdk/tools/emulator"
ANDROID="${HOME}/Android/Sdk/tools/android"
NAME="rabbitescape-${TARGET}"
TMP="/data/local/tmp"
${ANDROID} create avd \
--force \
--name "${NAME}" \
--target "${TARGET}" \
--abi "armeabi" \
--device "${DEVICE}"
# Start the emulator
${EMULATOR} -avd "${NAME}" &
# Wait for the device to boot and unlock it
${ADB} wait-for-device shell < ${TMP}/zero
getprop dev.bootcomplete > ${TMP}/bootcomplete
while cmp ${TMP}/zero ${TMP}/bootcomplete; do
{
echo -n "."
sleep 1
getprop dev.bootcomplete > ${TMP}/bootcomplete
}; done
echo "Booted."
exit
ENDSCRIPT
echo "Waiting 30 secs for us to be really booted"
sleep 30
echo "Unlocking screen"
${ADB} shell "input keyevent 82"
Now here is android-test - it launches the JUnit test code on the running emulator::
#!/bin/bash
set -x
set -u
set -e
PKGSUFFIX="$1" # E.g. "free"
APKNAME="$2" # E.g. "app-free-debug"
APPID="net.artificialworlds.rabbitescape${PKGSUFFIX}"
TESTAPPID="net.artificialworlds.rabbitescape${PKGSUFFIX}.test"
APK="rabbit-escape-ui-android/app/build/outputs/apk/${APKNAME}.apk"
TESTAPK="rabbit-escape-ui-android/app/build/outputs/apk/${APKNAME}-androidTest.apk"
ADB="${HOME}/Android/Sdk/platform-tools/adb"
DIR="/data/local/tmp/${APPID}"
TESTDIR="/data/local/tmp/${TESTAPPID}"
function run_test()
{
TMPFILE=$(mktemp)
${ADB} shell am instrument \
-w \
-r \
-e class "$1" \
"${TESTAPPID}/android.test.InstrumentationTestRunner" \
| tee ${TMPFILE}
egrep "OK (.* tests?)" ${TMPFILE}
}
${ADB} push "${APK}" "${DIR}"
${ADB} push "${TESTAPK}" "${TESTDIR}"
${ADB} shell pm install -r "${DIR}"
${ADB} shell pm install -r "${TESTDIR}"
run_test rabbitescape.ui.android.DialogsTest
run_test rabbitescape.ui.android.SmokeTest
run_test rabbitescape.ui.android.TestAndroidConfigUpgradeTo1
And here is android-stop-emulator - it shuts down the emulator:
#!/bin/bash
set -x
set -u
echo -e "auth $(cat ~/.emulator_console_auth_token)\nkill" \
| telnet localhost 5554
echo "Emulator stopped."