Skip to main content

Attachments

Attachments is a typed per-test bag for plugin-defined data. It sits alongside CapturedOutputs and CapturedInteractions on the test context but, unlike them, carries no framework semantics: Kensa neither validates nor renders the contents. A producer puts a value under a TypedKey; a tab renderer (or other consumer) retrieves it with the same key.

Use attachments when you are building a custom tab and need somewhere to stash data captured during the test that the renderer will format itself. The framework's first internal consumer is the UI testing module, which attaches captured browser screenshots for the screenshots tab.


When to use what

Capture mechanismWhen
CapturedOutputsThe value participates in assertions, sentence rendering, or the standard report tabs. Typed via CapturedOutput<T>.
CapturedInteractionsThe value is part of the sequence diagram. Pushed by interaction interceptors.
AttachmentsThe value is opaque to the framework — a tab plugin owns the rendering end-to-end.
LogQueryServiceThe data lives outside the JVM (log files, container stdout) and is fetched by correlation key after the run.

Example use cases

A few representative examples — each is something the framework has no reason to model, but a custom tab can render meaningfully:

  • Browser screenshots — captured during a UI test, rendered as an image gallery. (The first internal consumer; provided by the framework-uitesting module.)
  • HTTP request/response recordings — captured from a stub server (WireMock, MockServer) for replay or visual inspection in a "Recorded Calls" tab.
  • Generated business artifacts — PDFs, CSVs, generated images that the system under test produced during the run, surfaced for download or preview.
  • Failure-point diagnostics — on test failure, eagerly snapshot external state (a LogQueryService query result, a thread dump, a container description) and attach it. Freezes the data as it was at the moment of failure rather than at report-render time.
  • Trace/span exports — OpenTelemetry spans collected for the test, rendered in a custom waterfall view.

The common shape: capture during the test → put into attachments → a tab renderer pulls and formats it.


Defining a key and attaching data

Define a TypedKey once and share it between producer and consumer.

val mySnapshotKey = TypedKey<MySnapshot>("my-snapshot")

class SomeTest : KensaTest, WithKotest {

@Test
fun `something happens`() {
whenever { ctx ->
val snapshot = capture()
ctx.attachments.put(mySnapshotKey, snapshot)
}
then { /* assertions */ }
}
}

Two keys are equal iff their name values match — the type parameter is erased at runtime, so the name alone identifies the entry.


Reading from a tab renderer

A custom tab renderer receives the attachment via KensaTabContext.attachments:

class MyTabRenderer : KensaTabRenderer {
override fun render(ctx: KensaTabContext): String? {
val snapshot = ctx.attachments.getOrNull(mySnapshotKey) ?: return null
return formatAsHtml(snapshot)
}

override fun mediaType(): String = "text/html"
}

render returns null to omit the tab for this invocation — that is the normal "no data for this test" path; a renderer should return no content rather than fail.


API Reference

TypedKey<T>

data class TypedKey<T : Any>(val name: String)

Attachments methods

MethodDescription
put(key: TypedKey<T>, value: T)Store a value under the typed key
getOrNull(key: TypedKey<T>): T?Retrieve, returns null if absent