kotlinx-resources

Introduction: Kotlin Multiplatform (KMP) library for reading resources in tests
More: Author   ReportBugs   
Tags:

badge-library-version badge-plugin-version badge-jvm badge-js badge-nodejs badge-android badge-ios badge-watchos badge-tvos badge-macos badge-windows badge-linux

Kotlin Multiplatform (KMP) plugin and library for reading resources in tests.

It bridges the gap between different Kotlin Multiplatform targets, allowing you to access files from your resources folders in a single, consistent way.

Setup

Apply the plugin and add the library as a dependency in your build.gradle.kts:

plugins {
    id("com.goncalossilva.resources") version "<version>"
}

// ...

kotlin {
    sourceSets {
        val commonTest by getting {
            dependencies {
                implementation("com.goncalossilva:resources:<version>")
            }
        }
    }
}

Replace <version> with the latest version shown in the badge above.

Compatibility

Different Kotlin versions require different versions of the plugin/library:

Kotlin kotlinx-resources
2.1 and above 0.10 and above
2.0 0.9
1.9 and below 0.8 and below (plus k1 branch)

Usage

To access a file in your tests:

  1. Place it in a resources folder. For example, in src/commonTest/resources/ to have it available in all targets, or src/jsTest/resources/ to limit access to JS.
  2. Instantiate a Resource class with the path relative to that folder.

Basic Example

For a file located at src/commonTest/resources/data/example.json:

import com.goncalossilva.resources.Resource

class MyTest {
    @Test
    fun `example data exists`() {
        val resource = Resource("data/example.json")
        assertTrue(resource.exists())
    }

    @Test
    fun `example data ends in a newline`() {
        val content = Resource("data/example.json").readText()
        assertTrue(content.endsWith("\n"))
    }
}

API Overview

The Resource class provides a clean and simple API:

class Resource(path: String) {
    // Checks if the resource exists at the given path.
    fun exists(): Boolean

    // Reads the entire resource content as a string decoded using the specified charset.
    fun readText(charset: Charset = Charsets.UTF_8): String

    // Reads the entire resource content as a byte array.
    fun readBytes(): ByteArray
}

Example Project

Library tests use the library itself, so they serve as a practical example.

See ResourceTest for example usage, and resources-test/src/commonTest/resources for the associated folder structure for resources.

Caveats

Collisions

As a rule of thumb, place test files in src/commonTest/resources/. This avoids collisions entirely.

But if you want to override a common file, you can have a platform-specific version of it in the platform-specific source set (e.g., src/jvmTest/resources/). By default, Gradle will throw a "Entry (...) is a duplicate" error during the build process, prompting you to set a duplicateStrategy in your build.gradle.kts.

To have platform-specific resources override common ones, set the strategy to EXCLUDE:

tasks.withType<Copy>().configureEach {
    if (name.contains("Test(?:Copy|Process)Resources$".toRegex())) {
        duplicatesStrategy = DuplicatesStrategy.EXCLUDE
    }
}

Other DuplicatesStrategy options are available, but avoid INCLUDE, as the override behavior becomes inconsistent across platforms.

Browser limitations

In browser runtimes, readBytes() uses a synchronous XHR-based implementation to keep the API synchronous. Some browser+tooling stacks can corrupt leading UTF-16 BOM bytes (0xFF 0xFE / 0xFE 0xFF), which makes readText(Charsets.UTF_16) unreliable for UTF-16 files that include a BOM. Prefer Charsets.UTF_16LE / Charsets.UTF_16BE (or files without a BOM) for browser tests.

Acknowledgements

This library is inspired by this gist by @dellisd.

License

Released under the MIT License.

Apps
About Me
GitHub: Trinea
Facebook: Dev Tools