简体   繁体   中英

Kotlin-multiplatform: How to execute iOS unit tests

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 (eg 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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM