Fixtures
Kensa Fixtures are collections of type-safe, lazily-created test data values. Each test invocation has its own discreet set of Fixtures. They are shared across given, whenever, and then steps via the context objects.
The code on this page is taken from clearwave-example — a complete working project that demonstrates fixtures, captured outputs, sequence diagrams, and async assertions.
Defining Fixtures
Fixtures must be defined inside a FixtureContainer object. This can be an instance of a Java class or a Kotlin object.
They carry a string key which must be unique — and a factory that creates the value.
Fixtures can depend on up to three other fixtures; the factory for dependent fixtures will receive the resolved parent values at creation time.
It is best to define fixtures as static/public properties, which you can then import by name in your tests.
Primary Fixtures
A primary fixture has no dependencies — its factory takes no arguments.
The key design principle is granularity: define one fixture per meaningful field rather than one fixture per domain object. This lets each field appear by name in the rendered report wherever it is referenced in the test body.
- Kotlin
- Java
loading...
Java tests access fixtures via static aliases that delegate to the FixtureContainer singleton. Import them statically for idiomatic SCREAMING_SNAKE_CASE usage in test code.
loading...
Secondary Fixtures
A secondary fixture depends on one or more parent fixtures. Its factory receives the resolved parent values.
2 parents:
- Kotlin
- Java
loading...
createFixture("Appointment Slot", appointmentDate, appointmentTimeSlot,
(date, slot) -> new AppointmentSlot(date, slot))
3 parents — composite object built from individual field fixtures:
- Kotlin
- Java
loading...
createFixture("Voice Profile", voiceDownloadSpeed, voiceUploadSpeed, voiceSupplier,
(dl, ul, sup) -> new LineProfile("FTTP", dl, ul, "Full Fibre 900 with Voice", sup))
More than 3 parents — construct a SecondaryFixture directly; its factory lambda receives the full Fixtures map:
- Kotlin
- Java
loading...
new SecondaryFixture<>(
"Service Address",
fixtures -> new ServiceAddress(
fixtures.get(postcode), fixtures.get(addressLine1),
fixtures.get(town), fixtures.get(county)
),
Parents.Three.of(postcode, addressLine1, town)
)
Using Fixtures in Tests
In given and whenever actions
Access fixtures through the context destructured in each action lambda:
- Kotlin
- Java
loading...
loading...
loading...
loading...
In the test body — the key rendering pattern
The most important place to use fixture references is directly in the test body, passed as named arguments into assertion helpers. Kensa parses the test source code and when it sees fixtures[voiceDownloadSpeed] (Kotlin) or fixtures(VOICE_DOWNLOAD_SPEED) (Java) in the sentence it substitutes the fixture's display name — Voice Download Speed — rather than the raw value 900. This makes reports self-documenting.
- Kotlin
- Java
loading...
loading...
loading...
loading...
The assertion helpers simply accept the fixture values as ordinary parameters — they have no special knowledge of Kensa:
- Kotlin
- Java
loading...
loading...
loading...
loading...
Grouping Fixtures with WithFixturesSuite
When many tests share a large FixtureContainer, importing every fixture individually creates noise. WithFixturesSuite lets you declare one shared interface per container; test classes implement that interface and gain scoped, IDE-assisted block access to all its fixtures.
WithFixturesSuite is a Kotlin-only feature. Java tests access fixtures via static imports directly.
Without WithFixturesSuite — each test file imports fixtures individually:
import dev.kensa.fixture.TelecomsFixtures.AccountNumber
import dev.kensa.fixture.TelecomsFixtures.LineProfile
import dev.kensa.fixture.TelecomsFixtures.AppointmentSlot
// ... one import per fixture
class FeasibilityServiceTest : KensaTest(), WithHamkrest {
@Test
fun `can check feasibility`() {
then(theAccountNumber(), equalTo(fixtures[AccountNumber]))
}
}
With WithFixturesSuite — one shared interface, block syntax in tests:
Step 1 — declare the interface once (e.g. in a shared support file):
interface WithTelecomsFixtures : WithFixturesSuite<TelecomsFixtures> {
override val fixturesObject get() = TelecomsFixtures
}
Step 2 — implement it in test classes; access fixtures via a block lambda:
class FeasibilityServiceTest : KensaTest(), WithTelecomsFixtures, WithHamkrest {
@Test
fun `can check feasibility`() {
then(theAccountNumber(), equalTo(fixtures { AccountNumber }))
}
private fun theAccountNumber() = StateCollector { fixtures { AccountNumber } }
}
The block lambda (fixtures { AccountNumber }) is scoped to the FixtureContainer type, so the IDE offers autocomplete over exactly those fixtures — nothing more.
WithFixturesSuite API
| Member | Description |
|---|---|
val fixturesObject: F | Override to return the FixtureContainer singleton |
fun <T> fixtures(block: F.() -> Fixture<T>): T | Retrieves (lazily creates) a fixture via a scoped block |
Fixture API Reference
fixture() factory (Kotlin)
// Primary — no dependencies
fun <T> fixture(key: String, highlighted: Boolean = false, factory: () -> T): PrimaryFixture<T>
// Secondary — 1 parent
fun <T, P1> fixture(key: String, parent: Fixture<P1>, highlighted: Boolean = false, factory: (P1) -> T): SecondaryFixture<T>
// Secondary — 2 parents
fun <T, P1, P2> fixture(key: String, parent1: Fixture<P1>, parent2: Fixture<P2>, highlighted: Boolean = false, factory: (P1, P2) -> T): SecondaryFixture<T>
// Secondary — 3 parents
fun <T, P1, P2, P3> fixture(key: String, parent1: Fixture<P1>, parent2: Fixture<P2>, parent3: Fixture<P3>, highlighted: Boolean = false, factory: (P1, P2, P3) -> T): SecondaryFixture<T>
createFixture() factory (Java)
createFixture(String key, Supplier<T> factory)
createFixture(String key, boolean highlighted, Supplier<T> factory)
createFixture(String key, Fixture<P1> parent, Function<P1, T> factory)
createFixture(String key, Fixture<P1> parent1, Fixture<P2> parent2, BiFunction<P1, P2, T> factory)
Fixtures map
| Method / operator | Description |
|---|---|
fixtures[fixture] | Get (and lazily create) the fixture value |
fixtures.values() | All fixture values as List<NamedValue> |
fixtures.highlightedValues() | Only highlighted fixture values |
Highlighting
Set highlighted = true on any fixture to have its value appear prominently in the report. This is useful for correlation IDs and other values that should stand out across all interactions.
- Kotlin
- Java
loading...
createFixture("Tracking Id", /* highlighted = */ true, TrackingId::new)
Highlighted values are also accessible separately via fixtures.highlightedValues(), which Kensa uses to render them at the top of the report.