Skip to main content

Annotations

Kensa annotations control what appears in the HTML report. They are grouped here by purpose.


Rendering Values

These annotations cause field or method values to be captured and displayed in the report.

@RenderedValue

Captures a field, property, or method return value and displays it in the report using the identifier name as the label.

Targets: FIELD, VALUE_PARAMETER, FUNCTION, PROPERTY_GETTER

class PaymentTest : KensaTest, WithKotest {

@RenderedValue
private val paymentAmount = Money(100, "GBP")

@RenderedValue
private val merchantId = "merchant-42"
}

@ExpandableRenderedValue

Renders only the return value of the annotated method or property — the body is hidden. Use it when many fields need verification but they are not individually meaningful as named matchers; the data itself is the unit of meaning. The method may also perform comparison work; only the return value reaches the report.

When a field is domain-important on its own, prefer a named matcher (see Writing Fluent Tests).

Parameters:

ParameterTypeDefaultDescription
renderAsRenderedValueStyleDefaultDefault (value renderer / flat list) or Tabular (table layout)
headersArray<String>[]Column headers when renderAs = Tabular

Targets: FIELD, VALUE_PARAMETER, FUNCTION, PROPERTY_GETTER


Default style

Renders the return value via its registered value renderer. If the value is iterable, the renderer is invoked on each item and items appear as a flat list in the report. Use this when the items are meaningful values on their own — enum states, identifiers, or labels — and you want each one visible without a table:

@ExpandableRenderedValue
private fun theDispatchedLifecycle(): List<DispatchStatus> {
val actual = courier.observedLifecycle()
actual shouldContainExactly listOf(ACKNOWLEDGED, COMMITTED, DISPATCHED, DELIVERED)
return actual
}

// In the test:
then(theShipment(), shouldHaveCompletedDispatch())
and(theDispatchedLifecycle())

The report expands to show each DispatchStatus value; the test body shows one call. The helper body is not rendered.


Tabular style

Renders the return value as a labelled table. The default table-renderer behaviour: an Iterable<Pair<*, *>> becomes two-column rows. Provide explicit headers to label the columns. For richer table shapes, register a custom TableRenderer<T> for your type.

Use this when you need to verify a full set of named fields and want the BA to see field name alongside expected value:

@ExpandableRenderedValue(renderAs = Tabular, headers = ["Field", "Expected"])
private fun theShipmentFields(): List<Pair<String, String>> {
val dispatched = courier.lastDispatchedShipment()
return listOf(
"PostCode" to fixtures[PostCodeFx],
"CountryCode" to fixtures[CountryCodeFx],
"ServiceLevel" to fixtures[ServiceLevelFx],
// all fields
).also { fields ->
fields.forEach { (field, expected) ->
dispatched.field(field) shouldBe expected
}
}
}

// In the test:
then(courier.hasDispatched(aShipmentWith(theShipmentFields())))

The report shows a two-column table headed "Field / Expected". The test body stays a single line.


When to use @ExpandableRenderedValue vs a named matcher

SituationUse
Field is domain-important on its ownNamed matcher — aPostCode of value
Many fields; collection is the unit of meaning; items are meaningful values@ExpandableRenderedValue (Default)
Many fields; want a labelled table of field name to expected value@ExpandableRenderedValue(renderAs = Tabular)

See Writing Fluent Tests for the full narrative, worked bad/good examples, and the review checklist.


@RenderedValueContainer

Marks a field or parameter whose own fields should be rendered recursively. Useful for fixture or context objects.

Targets: FIELD, VALUE_PARAMETER

@RenderedValueContainer
private val paymentRequest: PaymentRequest = PaymentRequest(amount, currency, merchantId)
// Fields of PaymentRequest annotated with @RenderedValue will be captured

@RenderedValueWithHint

Adds a technical hint (e.g., a JSON path or XPath expression) alongside a rendered value. Repeatable — multiple hints can be applied to the same target.

Resolution is hierarchy-aware: a directive declared for a supertype (class or interface) applies to every subtype, unless a more specific directive is also declared. This means a single annotation on a sealed parent or common interface covers its whole hierarchy.

Parameters:

ParameterTypeDefaultDescription
typeKClass<*>The type this hint applies to. Matches the exact type and all subtypes; the most specific declared type wins.
valueStrategyRenderedValueStrategyUseIdentifierNameHow to extract the display value
valueParamString""Property/method name when strategy requires it
hintStrategyRenderedHintStrategyNoHintHow to extract the hint
hintParamString""Property/method name when hint strategy requires it

RenderedValueStrategy values:

ValueDescription
UseIdentifierNameUse the identifier name as the label
UseToStringCall toString() on the value
UsePropertyCall the property named by valueParam
UseMethodCall the method named by valueParam

RenderedHintStrategy values:

ValueDescription
NoHintNo hint
HintFromPropertyUse the property named by hintParam
HintFromMethodUse the method named by hintParam

Sentence Structure

@ExpandableSentence

Marks a method so that its body is expanded inline in the HTML report sentence, rather than shown as a single step.

Targets: FUNCTION

@ExpandableSentence
fun aRegisteredCustomerWithId(id: String): Action<GivensContext> = Action { ctx ->
ctx.fixtures[customer] // sentence expands to show the inner steps
}

Reporting

@Notes

Attaches a note to a test class. Rendered as a styled card above the test list in the HTML report. The value supports inline markdown for rich text, including links to external URLs and internal report navigation.

Targets: CLASS

Supported markdown syntax:

SyntaxResult
**text**Bold
*text*Italic
__text__Underline
~~text~~Strikethrough
[label](https://...)External link (opens in new tab)
[label](#methodName)Scroll to and expand a test method in the same suite
[label](#ClassName)Navigate to the suite whose simple class name matches
[label](#ClassName.methodName)Navigate to that suite and expand the named method

Internal link resolution:

  • #methodName — matches a test method by name within the current suite
  • #ClassName — matches by simple class name (e.g., #AdoptionServiceTest matches dev.kensa.example.AdoptionServiceTest)
  • #ClassName.methodName — combines both: navigate to suite, then expand method

Simple example:

@Notes("Payment gateway uses idempotency keys — retries with the same key are safe.")
class PaymentTest : KensaTest, WithKotest { ... }

Multiline example with formatting and links:

Kotlin annotation values must be compile-time constants, so .trimIndent() cannot be used. Start content on the line immediately after the opening """ with no leading indentation. Blank lines become paragraph breaks; single newlines within a paragraph become line breaks.

@Notes("""
**Payment gateway** uses *idempotency keys* — retries with the same key are __safe__.
See [Stripe docs](https://stripe.com/docs/idempotency) for details.

~~Direct refunds are no longer supported.~~ Use the [refund flow](#processRefund) instead.
For error-path behaviour see [RefundEdgeCasesTest](#RefundEdgeCasesTest).
""")
class PaymentTest : KensaTest, WithKotest { ... }

@Highlight

Highlights a specific field, parameter, or method return value throughout the report output.

Parameters:

ParameterTypeDefaultDescription
valueString""Optional label override

Targets: FUNCTION, FIELD, VALUE_PARAMETER, PROPERTY_GETTER

@Highlight
private val transactionId = "txn-abc-123"

@Issue

Links a test or class to one or more issue tracker tickets. Kensa appends the key to the URL configured via issueTrackerUrl.

Targets: CLASS, FUNCTION (repeatable)

@Issue("PROJ-42", "PROJ-43")
@Test
fun `refund is processed within 24 hours`() { ... }

@Sources

Tells Kensa to parse additional source classes referenced in your tests (e.g., shared helper objects). Required when sentence extraction needs to follow into classes outside the test file.

Targets: CLASS

@Sources(PaymentSteps::class, OrderFixtures::class)
class PaymentTest : KensaTest, WithKotest { ... }

UI Behaviour

@AutoOpenTab

Sets which report tab is open by default for a specific test or class. Overrides the global autoOpenTab configuration.

Targets: FUNCTION, CLASS

@AutoOpenTab(Tab.SequenceDiagram)
class OrderFlowTest : KensaTest, WithKotest { ... }

Tab values: CapturedInteractions, CapturedOutputs, Givens, Parameters, SequenceDiagram, None


@UseSetupStrategy

Overrides the global setupStrategy for a specific test or class, controlling how setup interactions appear in the sequence diagram.

Targets: FUNCTION, CLASS

@UseSetupStrategy(SetupStrategy.Grouped)
@Test
fun `order is fulfilled via warehouse`() { ... }

SetupStrategy values: Grouped, Ungrouped, Ignored — see Configuration for details.