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)
)
Parameter-Derived Fixtures
A parameter fixture derives its value per invocation from a named parameter of a parameterised test. Like every fixture it is registered by name, so it still resolves in fixtures[…] interpolation and renders by its display name — but instead of a fixed factory it is bound to a test parameter (by name) and a transform. Kensa seeds it from the invocation's arguments before the test body runs, so the value is available both in the test body and in the rendered sentence.
- Kotlin
- Java
object GreetingFixtures : FixtureContainer {
// bound to the `userName` parameter; the transform runs once per invocation
val Greeting = parameterFixture("Greeting", from = "userName") { name: String ->
"Hello, ${name.replaceFirstChar(Char::uppercase)}"
}
}
@ParameterizedTest
@ValueSource(strings = ["alice", "bob"])
fun `greets the user`(userName: String) {
then(theBanner(), equalTo(fixtures[Greeting])) // "Hello, Alice" / "Hello, Bob"
}
public class GreetingFixtures implements FixtureContainer {
public static final ParameterFixture<String> GREETING =
createParameterFixture("Greeting", "userName", (String name) -> "Hello, " + name);
}
@ParameterizedTest
@ValueSource(strings = {"alice", "bob"})
void greetsTheUser(String userName) {
then(theBanner(), is(fixtures(GREETING))); // "Hello, alice" / "Hello, bob"
}
The from value must match the test method's parameter name. The fixture is seeded only for invocations that actually supply that parameter; reading it in any other context raises a clear error. Secondary fixtures may depend on a parameter fixture — they resolve the seeded value like any other parent.
A parameter fixture derives from a single parameter. Reach for one when a value must be both computed from a test argument and referenced by name in the report; for a value you only need inside the test, use the parameter directly or a captured output.
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>
// Parameter-derived — bound to a test parameter by name
fun <T, P> parameterFixture(key: String, from: String, highlighted: Boolean = false, transform: (P) -> T): ParameterFixture<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)
// Parameter-derived — bound to a test parameter by name
createParameterFixture(String key, String from, Function<P, T> transform)
createParameterFixture(String key, String from, boolean highlighted, Function<P, T> transform)
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.