Skip to content

TestExecutionListener writes output and prints banner on empty test plans #140

@paul-brooks

Description

@paul-brooks

Symptom

When kensa-core lands on a test classpath that runs zero Kensa tests, the JUnit Platform listener (META-INF/services-registered) still:

  1. Recreates <outputDir> (deletes any prior contents — IoUtil.recreate(...) from ResultWriter's init block).
  2. Writes index.html, kensa.js, logo.svg, configuration.json, indices.json (with empty indices).
  3. Prints Kensa Output : <outputDir>/index.html to stdout.

This surfaces in the gradle plugin as kensa-dev/build-plugins#3 — non-Kensa test tasks emit the banner even when nothing in them used Kensa.

Why it's not solvable purely build-plugin-side

In the gradle plugin's pattern 2 (@ExpandableSentence support code in main only — compiler plugin applied to main, tests don't use the macros), the gradle plugin adds kensa-core as implementation to main. That dependency flows transitively to testRuntimeClasspath via Gradle's standard configuration inheritance. JUnit Platform auto-discovers and instantiates KensaTestExecutionListener from the META-INF/services/... entry before any build-side code can intervene — so the listener will fire on the test task even if that task is not configured as a Kensa output source set. The listener itself has to be defensive.

Fix

In core/src/main/kotlin/dev/kensa/context/KensaLifecycleManager.kt:88-91, short-circuit writeAllResults() on empty containers before resultWriter.value is accessed:

fun writeAllResults() {
    if (!isOutputEnabled) return
    if (kensaContext.testContainers.isEmpty()) return  // <-- new
    resultWriter.value.write(kensaContext.testContainers)
}

Ordering matters. ResultWriter's init block calls IoUtil.recreate(configuration.outputDir), so the guard must precede .value triggering construction — otherwise an empty plan still nukes the output directory.

One change covers JUnit 5, JUnit 6, TestNG, and Kotest — all four framework listeners funnel through writeAllResults():

  • frameworks/junit/junit5/src/main/kotlin/dev/kensa/junit/KensaTestExecutionListener.kt:25
  • frameworks/junit/junit6/src/main/kotlin/dev/kensa/junit/KensaTestExecutionListener.kt
  • frameworks/testng/src/main/kotlin/dev/kensa/testng/KensaTestNgListener.kt
  • frameworks/kotest/src/main/kotlin/dev/kensa/kotest/KensaKotestListener.kt

Verification

Add a regression test (likely in frameworks/junit/junit5/src/.../...Test.kt — pick the existing test harness file that hosts an end-to-end JUnit Platform invocation):

  1. Run an empty JUnit 5 test plan (or a plan with only non-Kensa tests) with kensa-core on the classpath.
  2. Assert outputDir was not created/touched.
  3. Assert captured stdout does not contain Kensa Output.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions