Michael Evans

A bunch of technobabble.

Accessing Test Resources Using Kotlin Multiplatform

| Comments

Kotlin Multiplatform (KMP) offers a powerful way to share code across platforms like Android, iOS, and the JVM. However, when it comes to testing, you might encounter a common challenge: how to handle test resources such as JSON files, configurations, or other data needed for tests. In this post, we’ll dive into strategies for accessing test resources in KMP projects.

Why Test Resources Matter

Test resources are essential for validating your code against real-world scenarios. For example, if you’re writing a library to parse JSON, you’d want to test it against diverse JSON samples representing different edge cases. While resource access is straightforward in single-platform projects, KMP’s multi-target nature requires some additional setup.

The Challenge in Kotlin Multiplatform

In KMP, test code resides in the commonTest source set, but resources aren’t directly bundled with it. Each platform manages file paths and resource access differently, so you need platform-specific setups for your resources and shared logic to load them efficiently.

Organizing Test Resources

Start by structuring your resources in a way that aligns with your project layout:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
project-root/
  src/
    commonTest/
      kotlin/
        ...
    androidTest/
      resources/
        test-data.json
    jvmTest/
      resources/
        test-data.json
    iosTest/
      resources/
        test-data.json

This setup ensures that each target platform can access its respective resources while keeping them logically grouped.

Loading Resources by Platform

For Android, place your test resources in the androidTest/resources directory. Use the javaClass.classLoader to load them:

1
2
3
4
5
fun loadTestResource(resourceName: String): String {
    val inputStream = javaClass.classLoader?.getResourceAsStream(resourceName)
    return inputStream?.bufferedReader()?.use { it.readText() }
        ?: throw IllegalArgumentException("Resource not found: $resourceName")
}

For iOS, add your resources to the test target in Xcode. Use platform-specific code to load them:

1
2
3
4
5
6
7
8
import platform.Foundation.*

fun loadTestResource(resourceName: String): String {
    val bundle = NSBundle.bundleForClass(MyTestClass::class)
    val path = bundle.pathForResource(resourceName, ofType = null)
        ?: throw IllegalArgumentException("Resource not found: $resourceName")
    return NSString.stringWithContentsOfFile(path, encoding = NSUTF8StringEncoding, error = null) as String
}

Sharing the Resource Loader

To avoid duplication, define a common function in commonTest and use the expect/actual pattern for platform-specific implementations:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// commonTest
expect fun loadTestResource(resourceName: String): String

// androidTest
actual fun loadTestResource(resourceName: String): String {
    val inputStream = javaClass.classLoader?.getResourceAsStream(resourceName)
    return inputStream?.bufferedReader()?.use { it.readText() }
        ?: throw IllegalArgumentException("Resource not found: $resourceName")
}

// iosTest
actual fun loadTestResource(resourceName: String): String {
    val bundle = NSBundle.bundleForClass(MyTestClass::class)
    val path = bundle.pathForResource(resourceName, ofType = null)
        ?: throw IllegalArgumentException("Resource not found: $resourceName")
    return NSString.stringWithContentsOfFile(path, encoding = NSUTF8StringEncoding, error = null) as String
}

Putting It All Together

With this setup, your commonTest code can seamlessly access resources across platforms. For example:

1
2
3
4
5
6
@Test
fun testJsonParsing() {
    val jsonData = loadTestResource("test-data.json")
    val parsedData = parseJson(jsonData)
    assertEquals(expectedData, parsedData)
}

Accessing test resources in Kotlin Multiplatform involves a bit of extra setup, but the payoff is a unified testing strategy across platforms. By leveraging the expect/actual pattern, you can create platform-specific resource loaders while keeping your test logic shared and consistent. Dive into KMP’s capabilities, and streamline your tests for every target platform!

Comments