Kotlin-multiplatform:如何执行 iOS 单元测试

Posted

技术标签:

【中文标题】Kotlin-multiplatform:如何执行 iOS 单元测试【英文标题】:Kotlin-multiplatform: How to execute iOS unit tests 【发布时间】:2019-05-29 02:42:06 【问题描述】:

我正在开发适用于 androidios 的 Kotlin 多平台库。我想编写一些特定于平台的单元测试。测试在共享代码和 Android 上按预期运行,但在 iOS 上不正常。

在共享代码模块的build.gradle文件下方。

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

SharedCode模块的结构是:

└── src
    ├── commonMain
    │   └── kotlin
    ├── commonTest
    │   └── kotlin
    ├── androidMain
    │   └── kotlin
    ├── androidTest
    │   └── kotlin
    ├── iOSMain
    │   └── kotlin
    └── iOSTest
        └── kotlin

androidTestcommonTest 文件夹中添加的测试确实按预期运行,但 iOSTest 中添加的测试没有运行。

但是,如果我将 fromPreset(iOSTarget, 'iOS') compilations.main.outputKinds('FRAMEWORK') 行替换为 fromPreset(presets.macosX64, 'macos') 并相应地更新目录名称,macosTest 文件夹中的测试会按预期运行。

为什么在构建 iOS 框架时无法运行 iOS 测试?关于我做错了什么或如何使它起作用的任何想法? :)

【问题讨论】:

【参考方案1】:

目前kotlin-multiplatform 插件仅支持在主机平台(例如 macOS 或 Windows)上运行测试。但是你可以手动添加一个任务来在模拟器上执行 iOS 测试:

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
        
    

查看完整的构建脚本here。

【讨论】:

太棒了,@IlyaMatveev 这就是我想要的。谢谢! :D 它会打开 ios 模拟器并运行测试吗?在我的情况下,它不会像我在 xcode 项目中运行测试时那样打开 ios 模拟器。 @mkkrolik,不,它不会打开设备模拟​​器,而是在模拟器的进程中执行测试。【参考方案2】:

当我遇到一些问题时,我会在这里发布我的解决方案。

使用 Kotlin 1.3.50 和 XCode 11 我不得不更改我的命令行参数:

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)

【讨论】:

这应该被标记为最新版本的 kotlin 和 xcode 的答案【参考方案3】:

@IlyaMatveev 的回答非常适合我。但我不得不使用 Kotlin 版本 1.3.41 更新两行:

dependsOn 'linkTestDebugExecutableIos' 就是现在 dependsOn 'linkDebugTestIos'

def binary = kotlin.targets.ios.binaries.getExecutable('test', 'DEBUG').outputFile 现在是def binary = kotlin.targets.ios.binaries.getTest("DEBUG").outputFile

【讨论】:

以上是关于Kotlin-multiplatform:如何执行 iOS 单元测试的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Kotlin-Multiplatform 在 iOS 应用程序的后台线程中运行任务?

Kotlin-Multiplatform 中的 CPointer

在 kotlin-multiplatform 上生成 UUID?

Mocha 如何执行“it”调用?如何同步执行测试?

交叉验证是如何执行的以及 GridSearchCV() 具体是如何执行的?

如何查看执行计划