Question

How to Read Files from src/test/resources in Kotlin

kotlinfile-ioclasspathclassloader

Question

I want to write a Spek test in Kotlin and need to read an HTML file from the src/test/resources folder.

For example, if the file is located at src/test/resources/html/file.html, how can I load its contents into a String inside a test setup block?

class MySpec : Spek({
    describe("blah blah") {
        given("blah blah") {
            var fileContent: String = ""

            beforeEachTest {
                // How do I read html/file.html from src/test/resources?
                fileContent = ...
            }

            it("should blah blah") {
                // assertions here
            }
        }
    }
})

Short Answer

By the end of this page, you will understand how Kotlin tests access files in src/test/resources, how to read those files safely as text, and which resource-loading approach to use in real test code. You will also see practical examples for Spek and general Kotlin test setups.

Concept

In Kotlin and Java projects, files inside src/test/resources are not usually accessed by hardcoded filesystem paths during tests. Instead, they are placed on the test classpath by the build tool.

That means the usual way to read them is:

  • locate the resource by its classpath path, such as html/file.html
  • open it as a stream or URL
  • convert it to a String

This matters because classpath-based loading is more reliable than using direct paths like:

File("src/test/resources/html/file.html")

A direct path may work on your machine, but it can fail when:

  • the working directory changes
  • tests run in CI
  • the project is packaged differently
  • the code runs from a JAR or another environment

Using the class loader is the standard approach because it works with how test resources are actually provided to your code.

In Kotlin tests, the most common pattern is:

val text = javaClass.getResource("/html/file.html")!!.readText()

or:

val text = javaClass.classLoader
    .getResourceAsStream("html/file.html")!!
    .bufferedReader()
    .use { it.readText() }

Both approaches read a file from the classpath. The important idea is that src/test/resources becomes the root of the resource path during test execution.

Mental Model

Think of src/test/resources as a special storage shelf that your test runner packs into a toolbox before your tests start.

You do not usually walk to the original shelf location by file path. Instead, you ask the toolbox:

  • “Do you have html/file.html?”
  • If yes, give me its contents.

So:

  • src/test/resources = the source shelf in your project
  • classpath = the packed toolbox available at runtime
  • getResource(...) = asking the toolbox for a named item

This is why resource loading uses names like html/file.html instead of full source-folder paths.

Take Quiz

Syntax and Examples

The most common way to read a text resource in Kotlin is with getResource or getResourceAsStream.

Option 1: Using getResource(...).readText()

val fileContent = object {}.javaClass.getResource("/html/file.html")!!.readText()

What this does

  • getResource("/html/file.html") looks for the file on the classpath
  • the leading / means the path starts from the classpath root
  • !! means you expect the file to exist; if not, the test fails immediately
  • readText() reads the file contents into a String

Option 2: Using getResourceAsStream(...)

val fileContent = object {}.javaClass.classLoader
    .getResourceAsStream("html/file.html")!!
    .bufferedReader()
    .use { it.readText() }

Why use this version?

This is a very common Java/Kotlin pattern and works well when you want explicit stream handling.

Spek example

class MySpec : Spek({
    describe("blah blah") {
        given("blah blah") {
            var fileContent = ""

            beforeEachTest {
                fileContent = MySpec::class.java
                    .getResource("/html/file.html")!!
                    .readText()
            }

            it("should blah blah") {
                println(fileContent)
            }
        }
    }
})

Reusable helper function

If you read many test resources, create a helper:

fun readResource(path: String): String {
    return object {}.javaClass.getResource(path)?.readText()
        ?: error("Resource not found: $path")
}

Usage:

val html = readResource("/html/file.html")

Step by Step Execution

Consider this example:

val text = MySpec::class.java
    .getResource("/html/file.html")!!
    .readText()

Here is what happens step by step:

  1. MySpec::class.java

    • Gets the Java Class object for MySpec.
  2. .getResource("/html/file.html")

    • Asks the class loader to find a resource named html/file.html from the classpath root.
    • Because the file is in src/test/resources/html/file.html, it should be found during test execution.
  3. !!

    • If the resource is not found, getResource(...) returns null.
    • !! turns that into an immediate failure with a NullPointerException.
    • In tests, this can be acceptable because a missing test resource should fail fast.
  4. .readText()

    • Reads the entire file into a single String.
  5. The result is stored in text

    • You can now use text in assertions or parsing.

Full traceable example

val html = MySpec::class.java.getResource("/html/file.html")!!.readText()
println(html.length)

If the file contains:

<h1>Hello</h1>

Then:

  • the resource is found
  • the text "<h1>Hello</h1>" is loaded
  • html.length prints the number of characters in that string

Real World Use Cases

Reading test resources is common in many kinds of Kotlin projects.

Common use cases

  • HTML testing
    • load sample HTML pages and verify parsing or rendering logic
  • JSON API testing
    • store sample request/response payloads in src/test/resources
  • XML processing
    • test XML parsing with realistic fixture files
  • Template testing
    • compare generated output with expected text files
  • Data-driven tests
    • keep test input files separate from code for readability

Example: JSON fixture

val json = MySpec::class.java.getResource("/responses/user.json")!!.readText()

Example: expected HTML output

val expectedHtml = MySpec::class.java.getResource("/html/expected.html")!!.readText()
Take Quiz

Real Codebase Usage

In real projects, developers usually wrap resource loading in a small utility so tests stay clean.

Common patterns

1. Test fixture helper

object TestResources {
    fun readText(path: String): String {
        return TestResources::class.java.getResource(path)?.readText()
            ?: error("Missing resource: $path")
    }
}

Usage:

val html = TestResources.readText("/html/file.html")

2. Fail-fast validation

Tests often use error(...) or requireNotNull(...) so a missing fixture gives a clear message.

val url = requireNotNull(MySpec::class.java.getResource("/html/file.html")) {
    "Resource not found: /html/file.html"
}
val html = url.readText()

3. Keep fixtures realistic

Teams usually store:

  • API payload samples
  • rendered HTML snapshots
  • CSV imports
  • invalid input examples for error cases

4. Reuse across many test frameworks

The same resource-loading idea works in:

  • Spek
  • JUnit
  • Kotest
  • plain Kotlin test code

The framework usually does not change how classpath resources are loaded.

Common Mistakes

Here are common beginner mistakes when reading test resources in Kotlin.

1. Using the source folder path directly

Broken approach:

val text = java.io.File("src/test/resources/html/file.html").readText()

Why this is risky

  • depends on the current working directory
  • may fail in CI or different build setups
  • does not use the classpath

Better

val text = MySpec::class.java.getResource("/html/file.html")!!.readText()

2. Forgetting the correct resource path

Broken approach:

val text = MySpec::class.java.getResource("file.html")!!.readText()

If your file is actually in html/file.html, this will not find it.

Better

val text = MySpec::class.java.getResource("/html/file.html")!!.readText()

3. Confusing getResource and getResourceAsStream

Both work, but they return different things:

  • getResource(...) returns a URL?
  • getResourceAsStream(...) returns an InputStream?

Do not mix their APIs.

4. Ignoring nullability

Broken approach:

val text = MySpec::class.java.getResource("/html/file.html").readText()

This does not compile because getResource(...) can return null.

Better

val text = MySpec::class.java.getResource("/html/file.html")?.readText()
    ?: error("Resource not found")

5. Using the wrong leading slash

With Class.getResource(...):

  • "/html/file.html" means from the classpath root
  • "html/file.html" may be treated relative to the class package

For test resources, using the leading / is usually clearer.

6. Not closing streams

If you use getResourceAsStream(...), use use {}.

val text = MySpec::class.java.classLoader
    .getResourceAsStream("html/file.html")!!
    .bufferedReader()
    .use { it.readText() }

Comparisons

ApproachExampleBest forNotes
Class.getResource(...).readText()MySpec::class.java.getResource("/html/file.html")!!.readText()Simple text fixturesVery readable
ClassLoader.getResourceAsStream(...)classLoader.getResourceAsStream("html/file.html")Stream-based readingCommon and explicit
File("src/test/resources/...")File("src/test/resources/html/file.html")Quick local experimentsLess portable for real tests

getResource vs getResourceAsStream

MethodReturnsPath styleGood choice when
getResource(...)URL?Often uses /html/file.htmlYou want concise text loading
getResourceAsStream(...)InputStream?Usually html/file.htmlYou want stream handling or byte reading

Relative vs absolute resource paths

PathMeaning
"/html/file.html"Start at the classpath root
"html/file.html"May be relative to the current class package when using Class.getResource(...)

For beginners, an absolute classpath path is usually the safest option.

Take Quiz

Cheat Sheet

Quick reference

Read a test resource as text

val text = MySpec::class.java.getResource("/html/file.html")!!.readText()

Read with class loader

val text = MySpec::class.java.classLoader
    .getResourceAsStream("html/file.html")!!
    .bufferedReader()
    .use { it.readText() }

Safe version with custom error

val text = MySpec::class.java.getResource("/html/file.html")?.readText()
    ?: error("Resource not found: /html/file.html")

Rules to remember

  • src/test/resources becomes part of the test classpath
  • use classpath paths like html/file.html
  • with Class.getResource(...), "/" means classpath root
  • prefer resource loading over direct filesystem paths in tests
  • use use {} when working with streams

Common paths

If file is here:

src/test/resources/html/file.html

Use one of these:

getResource("/html/file.html")

or

getResourceAsStream("html/file.html")

Good test pattern

fun readResource(path: String): String =
    requireNotNull(MySpec::class.java.getResource(path)) {
        "Missing resource: $path"
    }.readText()

FAQ

How do I read a file from src/test/resources in Kotlin?

Use classpath resource loading, for example:

val text = MySpec::class.java.getResource("/html/file.html")!!.readText()

Why should I avoid File("src/test/resources/...") in tests?

Because it depends on the current working directory and is less reliable across environments.

What path should I use for a file in src/test/resources/html/file.html?

Usually either:

"/html/file.html"

with getResource(...), or:

"html/file.html"

with getResourceAsStream(...).

What happens if the resource does not exist?

getResource(...) or getResourceAsStream(...) returns null. You should handle that with requireNotNull, ?: error(...), or !! in tests.

Can I use this approach outside Spek?

Yes. The same pattern works in JUnit, Kotest, and regular Kotlin or Java code.

Is readText() suitable for HTML files?

Yes. HTML is plain text, so reading it into a String with readText() is a normal approach.

Should I use getResource or getResourceAsStream?

For simple text files, getResource(...).readText() is concise. If you want stream-based handling, use getResourceAsStream(...).

Take Quiz

Mini Project

Description

Create a small Kotlin test utility that loads HTML fixture files from src/test/resources and checks whether expected text appears in them. This mirrors a common real-world testing pattern where sample HTML files are stored as fixtures and reused across multiple tests.

Goal

Build a reusable resource reader and use it to load an HTML file from src/test/resources, then verify its contents in a test.

Requirements

  • Create an HTML file inside src/test/resources/html/.
  • Write a Kotlin helper function that reads a resource into a String.
  • Use the helper in a test to load the HTML file.
  • Assert that the loaded content contains expected text.
Take Quiz

Keep learning

Related questions

View more