Multiplatform Testing Plugin
A Gradle plugin for easy testing of Kotlin Multiplatform projects in your CI pipeline.
-
Support for testing
android()
targets on the emulator or a connected device. The plugin takes care of downloading SDK components, identifying the correct platform and system image, spinning up a well configured emulator and run the tests. -
Support for testing
androidNative*()
binaries on the emulator or a connected device. Just like JVM-based android targets, the plugin takes care of the emulator setup and ensures that the device will be able to run the target architecture.
Component | Status |
---|---|
Android JVM tests (Linux, amd64) | |
Android JVM tests (macOS, x64) | |
Android Native tests (Linux, amd64) | |
Android Native tests (macOS, x64) |
To run the plugin, Gradle 6.8+ is required.
Install
The plugin is available on Maven Central. This means you have to add it to the project classpath:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("io.deepmedia.tools.testing:plugin:0.4.0")
}
}
and then apply it to the desired project:
plugins {
id("kotlin-multiplatform")
id("io.deepmedia.tools.multiplatform-testing")
}
We also publish development snapshots with version latest-SNAPSHOT
in sonatype:
buildscript {
repositories {
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
}
dependencies {
classpath("io.deepmedia.tools.testing:plugin:latest-SNAPSHOT")
}
}
Android (JVM) targets
Requirements
A few things are required to test Android targets:
-
A X64 host machine. The plugin might work properly on ARM, but this has not been tested yet. If it works (emulator runs), it is unlikely that X86-based binaries can be executed successfully.
-
The Android SDK Command Line tools should be installed or, if they are not, you should provide a valid
sdkHome
directory in which they will be downloaded (macOS / Linux only).multiplatformTesting { androidTools { // path to installed SDK, or path where SDK will be installed // defaults to $ANDROID_HOME environment variable. sdkHome.set("path/to/sdk") } }
-
Hardware acceleration. While Android documentation states that it is "recommended", hosts without acceleration are typically unable to run the emulator at all.
Tasks
Use ./gradlew tasks --group='Multiplatform Testing' to list all testing tasks.
The plugin provides two relevant tasks:
run
tasks: runs tests for the specified target, typically this will beTests runAndroidTests
unless you used a custom name for the android target.killAndroidEmulators
task: kills all currently running emulators. Can be used to cleanup.
This means that the typical command will be:
./gradlew app:runAndroidTests app:killAndroidEmulators
Configuration
multiplatformTesting {
android {
// Enforce testing on a specific API level. If not set, we'll choose the API level in
// a way that minimizes the number of emulators and the download of new system images.
// Defaults to the MPT_ANDROID_API environment variable.
apiLevel.set(21)
// Enforce testing on a specific image tag. If not set, we'll choose the image tag in
// a way that minimizes the number of emulators and the download of new system images.
// Defaults to the MPT_ANDROID_TAG environment variable.
tag.set("google_apis")
// Choose the default variant that will be tested when running 'runAndroidTests'.
// Defaults to the MPT_ANDROID_VARIANT environment variable, falls back to "debug".
defaultVariant.set("debug")
// By default, run* tasks execute both instrumented tests and unit tests.
// Set this flag to false to avoid running unit tests.
includeUnitTests.set(false)
}
}
Android Native targets
Requirements
All the requirements for running Android JVM targets apply.
How it works
Running Android Native tests is a multi-step process and between these steps, the plugin applies workarounds to known issues to make testing as smooth and fast as possible.
- A K/N test executable is built. These executables are built by the Kotlin compiler with the -tr option and automatically run all test suites and print logs to Android logcat.
- K/N test executables are currently broken. The plugin workarounds this issue by treating them as shared libraries and loading them at runtime using
dlopen()
and passing appropriate arguments to trick the Kotlin runtime launcher. - The plugin looks for a connected device (real device or emulator) that is able to run the architecture.
- If not found, the plugin looks for existing AVDs and starts the first one that would work.
- If not found, the plugin downloads the needed packages from
sdkmanager
, creates an AVD and starts it. - The runner executable is executed using
adb shell
. This makes a huge difference with respect to e.g. JNI-based tests, as we don't have to wait for the emulator to be completely booted and care about all details (unlock, avoid welcome screens, animations...) that make testing hard in a JVM process. - Interesting logcat logs are printed to the host stdout.
Tasks
Use ./gradlew tasks --group='Multiplatform Testing' to list all testing tasks.
The plugin provides three types of tasks:
runAllAndroidNativeTests
task: runs tests for all androidNative* targets.run
tasks: runs tests for the specified target, e.g.Tests runAndroidNativeX86Tests
killAndroidEmulators
task: kills all currently running emulators. Can be used to cleanup.
Using the runAllAndroidNativeTests
tasks is recommended, because it ensures the most efficient emulator installation. This is because emulator images can run multiple architectures (for example: 64bit images might run 32bit binaries, and X86/64 images might run ARM binaries through binary translation). The plugin is aware of this and, if runAllAndroidNativeTests
is used, is able to pick up the best emulator for the job, saving time and resources.
This means that the typical command will be:
./gradlew app:runAllAndroidNativeTests app:killAndroidEmulators