I'm working on a Kotlin-multiplatform library for Android and iOS. I want to write some platform-specific unit test. The tests run as expected for the shared code and Android but not for iOS.
Below the build.gradle
file of the shared code module.
apply plugin: "kotlin-multiplatform"
kotlin {
targets {
final def iOSTarget = System.getenv('SDK_NAME')?.startsWith("iphoneos") \
? presets.iosArm64 : presets.iosX64
fromPreset(iOSTarget, 'iOS') {
compilations.main.outputKinds('FRAMEWORK')
}
fromPreset(presets.jvm, 'android')
}
sourceSets {
commonMain.dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-common"
}
commonTest.dependencies {
implementation 'org.jetbrains.kotlin:kotlin-test'
implementation 'org.jetbrains.kotlin:kotlin-test-junit'
}
androidMain.dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib"
}
androidTest {
dependencies {
implementation 'org.jetbrains.kotlin:kotlin-test'
implementation 'org.jetbrains.kotlin:kotlin-test-junit'
}
}
iOSMain.dependencies {
}
iOSTest.dependencies {
implementation 'org.jetbrains.kotlin:kotlin-test'
implementation 'org.jetbrains.kotlin:kotlin-test-junit'
}
}
}
// workaround for https://youtrack.jetbrains.com/issue/KT-27170
configurations {
compileClasspath
}
task packForXCode(type: Sync) {
final File frameworkDir = new File(buildDir, "xcode-frameworks")
final String mode = project.findProperty("XCODE_CONFIGURATION")?.toUpperCase() ?: 'DEBUG'
inputs.property "mode", mode
dependsOn kotlin.targets.iOS.compilations.main.linkTaskName("FRAMEWORK", mode)
from { kotlin.targets.iOS.compilations.main.getBinary("FRAMEWORK", mode).parentFile }
into frameworkDir
doLast {
new File(frameworkDir, 'gradlew').with {
text = "#!/bin/bash\nexport 'JAVA_HOME=${System.getProperty("java.home")}'\ncd '${rootProject.rootDir}'\n./gradlew \$@\n"
setExecutable(true)
}
}
}
tasks.build.dependsOn packForXCode
and the structure of the SharedCode
module is:
└── src
├── commonMain
│ └── kotlin
├── commonTest
│ └── kotlin
├── androidMain
│ └── kotlin
├── androidTest
│ └── kotlin
├── iOSMain
│ └── kotlin
└── iOSTest
└── kotlin
The tests added in the androidTest
and commonTest
folders do run as expected but the ones added in the iOSTest
do not run.
However, if I replace the the line fromPreset(iOSTarget, 'iOS') { compilations.main.outputKinds('FRAMEWORK') }
for fromPreset(presets.macosX64, 'macos')
and update the directory names accordly, the tests in the macosTest
folder do run as expected.
Why it is not possible to run iOS test when building iOS frameworks? Any idea about what I'm doing wrong or how I can make this works? :)
Currently the kotlin-multiplatform
plugin supports only running tests for host platforms (e.g. macOS or Windows). But you can manually add a task for executing iOS tests on a simualtor:
task iosTest {
def device = project.findProperty("iosDevice")?.toString() ?: "iPhone 8"
dependsOn 'linkTestDebugExecutableIos'
group = JavaBasePlugin.VERIFICATION_GROUP
description = "Runs tests for target 'ios' on an iOS simulator"
doLast {
def binary = kotlin.targets.ios.binaries.getExecutable('test', 'DEBUG').outputFile
exec {
commandLine 'xcrun', 'simctl', 'spawn', device, binary.absolutePath
}
}
}
See the full build script here.
As I ran into some issues, I'll post my solution here.
With Kotlin 1.3.50
and XCode 11
I had to change my command line arguments:
val iosTest: Task by tasks.creating {
val device = project.findProperty("iosDevice")?.toString() ?: "iPhone 8"
val testExecutable = kotlin.targets.getByName<KotlinNativeTarget>("iosX64").binaries.getTest("DEBUG")
dependsOn(testExecutable.linkTaskName)
group = JavaBasePlugin.VERIFICATION_GROUP
description = "Runs tests for target 'ios' on an iOS simulator"
doLast {
exec {
println(testExecutable.outputFile.absolutePath)
commandLine( "xcrun", "simctl", "spawn", "--standalone", device, testExecutable.outputFile.absolutePath)
}
}
}
tasks.getByName("allTests").dependsOn(iosTest)
The answer from @IlyaMatveev works perfect for me. But I had to updates two lines using Kotlin Version 1.3.41:
dependsOn 'linkTestDebugExecutableIos'
is now
dependsOn 'linkDebugTestIos'
def binary = kotlin.targets.ios.binaries.getExecutable('test', 'DEBUG').outputFile
is now def binary = kotlin.targets.ios.binaries.getTest("DEBUG").outputFile
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With