Skip to main content

iOS Tests

There are two ways to test KMM iOS code: testing with Kotlin only and integration testing from iOS. Here is an introduction to both methods.

LINK

To understand the basics of testing, please refer to the Test documentation first.

Testing with Kotlin only

If you want to test Kotlin code only, you can write tests in Kotlin as usual. You can use Koject.runTest() or Koject.startTest() to start a DI container for testing.

@Provides
class Controller
class SampleTest() {
@Test
fun test() = Koject.runTest {
val controller = inject<Controller>() // can be injected
}
}

Please refer to the Test documentation for more details.

iOS Unit Tests

To test including code written in Swift, use XCTest.

First, add the koject-test dependency to the iosMain source set in the build.gradle.kts file of the shared module. Use the main source set, not the test source set, to call from Swift.

shared/build.gradle.kts
kotlin {
/* ... */

sourceSets {
val commonMain by getting {
dependencies {
implementation("com.moriatsushi.koject:koject-core:1.3.0")
}
}

val iosMain by getting {
+ dependencies {
+ implementation("com.moriatsushi.koject:koject-test:1.3.0")
+ }
}
}
}

Create a KojectHelper object and add startTest() and stop() functions.

src/iosMain/kotlin/KojectHelper.ios.kt
object KojectHelper {
fun start() {
Koject.start()
}

fun startTest() {
Koject.startTest()
}

fun stop() {
Koject.stop()
}
}

You can test with the DI container for testing by calling startTest in setUpWithError and stop in tearDownWithError.

SampleStateTests.swift
import XCTest
@testable import ios
@testable import shared

final class SampleStateTests: XCTestCase {
override func setUpWithError() throws {
KojectHelper.shared.startTest()
}

override func tearDownWithError() throws {
KojectHelper.shared.stop()
}

func test() throws {
/* ... */
}
}

iOS UI Tests

When performing UI tests, the DI container for the application is used, so branching must be done on the application side.

First, create a test branch in your entry point with the @main attribute as follows:

MyApp.swift
import SwiftUI
import shared

@main
struct MyApp: App {
init() {
#if DEBUG
let isTesting = CommandLine.arguments.contains("TESTING")
if isTesting {
KojectHelper.shared.startTest()
} else {
KojectHelper.shared.start()
}
#else
KojectHelper.shared.start()
#endif
}

var body: some Scene {
/* ... */
}
}

Specify launchArguments when starting the test, and UI tests can be executed using the DI container for testing.

UITests.swift
import XCTest

final class UITests: XCTestCase {
let app = XCUIApplication()

func testSome() {
app.launchArguments = ["TESTING"]
app.launch()

/* ... */
}
}