Kotlin-multiplatform: How to execute iOS unit tests

kotlin-multiplatform kapt
kotlin multiplatform library
gradle kotlin sourcesets
kotlin multiplatform tutorial

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.

Kotlin Multiplatform Project: Unit tests for iOS and Android, Navigate to the androidApp directory, and just create a folder with the name fastlane and place a file named Fastfile inside it. And you are ready to go back to the command line and execute the same command as for the iOS project ( bundle exec fastlane tests ) to run the unit test for the Android app. The tests for iOS are not executed and this is happening because Kotlin Multiplatform Project plugin doesn’t support running tests on other than the host platforms, e.g iOS on macOS. To solve this issue we have to manually add a new Gradle task to run those iOS tests. This task will spawn a simulator process where the tests will be run.

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

Running Kotlin/Native unit tests on iOS Simulator, When developing Kotlin/Native solution one may need to run unit tests against a selected Our multiplatform solution is built as a simple Gradle-based project. iOS. To compile the project from Xcode just open iosApp/iosApp.xcodeproj and run the application. The swift tests also can be executed from Xcode. To compile a framework for ios simulator from the command line execute: > ./gradlew :greeting:build To compile the framework for a device use the device project property:

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)

Kotlin Multiplatform: testing a shared module supporting iOS and , The setup of unit tests for Multiplatform can be tricky, though. Android's JVM), kotlin-test-common will not suffice to be running your verification  Kotlin-Multiplatform (Android, iOS, JS & JVM Desktop App) This example shows how to create a simple Android/iOS/JVM/JS project sharing some Kotlin code. This app saves on a local database your favourites locations and get the current weather of them from OpenWeatherMap.

Kotlin Multiplatform Project: Unit tests for iOS and Android : Kotlin, Perform an unchecked cast to the requested typearg. Kotlin is already known as a language that mostly uses syntactic sugar to improve Java, while still ensuring​  This is necessary to execute the tests that we are going to create in an iOS emulator. And finally, we indicate the script command to run the build task. Among other subtasks, build task will compile the code of the library for the JVM, also for iOS and finally, It will run the tests on both platforms.

Kotlin Multiplatform Android/iOS: Testing, A how-to guide on writing tests within Kotlin Multiplatform. My Android app will use the suspend function, and my iOS app will Adding this expect and actual utility makes it easy to run our common test suspend functions. But then I noticed it created my test in the test package, which is the package for Android tests, instead of the commonTest package. Lesson learned: Create all your common tests manually in the correct package right now. Executing tests. After I finally wrote a test of course I wanted to execute it — but this would be too simple if it just

How to run commonTest in multiplatform project?, I am currently working on a kotlin mmp which for ios and android. The tests can run by right click the file or method, but cannot be trigger with gradle implementation kotlin('test-junit') implementation kotlin('test-common')  Multiplatform projects are an experimental feature in Kotlin 1.2 and 1.3. All of the language and tooling features described in this document are subject to change in future Kotlin versions. Working on all platforms is an explicit goal for Kotlin, but we see it as a premise to a much more important goal: sharing code between platforms.

Comments
  • Awesome, @IlyaMatveev this is what I was looking for. Thanks! :D
  • Does it open ios simulator and run a tests? In my case it doesn't open ios simulator as it do when i run a tests in xcode project.
  • @mkkrolik, no, it does not open the device simulator but tests are executed in the simulator's process.
  • @DiegoPalomar that makes sense, thanks.
  • this should be marked as answer for latest version of kotlin and xcode