jGleitz/testfiles

testfiles fails to be auto-registered in newer Kotest versions

Opened this issue · 0 comments

testfiles extension doesn't seem to be auto-registered in newer Kotest versions.

Background

My setup (I think) is quite simple:

build.gradle.kts:

dependencies {
    testImplementation("io.kotest:kotest-runner-junit5::5.6.2")
    testImplementation("de.joshuagleitze:kotest-files:2.0.0")
}

how I'm using it in an example test:

class Test : FreeSpec({

    "Some" - {
        "nested" - {
            "test" {
                val directory = testFiles.createDirectory()
            }
        }
    }
})

However, this failed for me on any subsequent build:

java.nio.file.FileAlreadyExistsException: /home/andrzej/work/xyz/build/test-outputs/test-176899501
	at java.base/sun.nio.fs.UnixException.translateToIOException(UnixException.java:94)
	at java.base/sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:106)
	at java.base/sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:111)
	at java.base/sun.nio.fs.UnixFileSystemProvider.createDirectory(UnixFileSystemProvider.java:397)
	at java.base/java.nio.file.Files.createDirectory(Files.java:700)
	at de.joshuagleitze.testfiles.DefaultTestFiles.createDirectory(DefaultTestFiles.kt:31)

Note that the directory is created under root test-outputs and not under test scope like described in README.

The issue

I attempted to debug this and found that Kotest initializes @AutoScan extensions in io.kotest:kotest-framework-engine-jvm, io.kotest.engine.config.applyConfigFromAutoScan.kt:16. However, to my surprise the scanning code doesn't discover testfiles' de.joshuagleitze.testfiles.kotest.KotestTestFilesAdapter object which I believe to be the testfiles' extension for Kotest. Because of that it doesn't register it as a Kotest extension, making testfiles run with ROOT_SCOPE context.

I don't have any environment variables set, Java properties set or Kotest-project-wide configuration that would be related to Kotest configuration.

Suspected reason

I think the reason are annotations:

  • Kotest during extension scanning uses ClassGraph which uses classfile bytecode for understanding which classes are matches for a given query. It uses the following query:

    classgraph().scan().use { result ->
        result.getClassesWithAnnotation(AutoScan::class.java.name)
            ...
    }
    

    The AutoScan here is io.kotest.core.annotation.AutoScan

  • testfiles' KotestTestFilesAdapter is annotated with io.kotest.core.spec.AutoScan (note the different package), which in newer Kotest versions is a type alias: typealias AutoScan = io.kotest.core.annotation.AutoScan

    I think the scanner, due to the different annotation package, doesn't consider testfiles' KotestTestFilesAdapter as a match.

A different problem

I though: hey, doesn't matter. I can use Kotest's project-wide configuration to manually register testfiles as its extension.

This doesn't work however, as testfiles' KotestTestFilesAdapter is internal and therefore I cannot reference it from my code.

Possible solution

  1. Migrate to updated Kotest annotation.
  2. Possibly make KotestTestFilesAdapter public so users can have fine-grained control over registration if they want to.