Skip to main content
A case is one input to your agent. You build cases with kensa_case and parametrize them into a pytest test. Each case carries an id, an input (or a conversation), and any extra fields your harness needs.
from kensa.pytest import kensa_case

kensa_case(id="refund_no_order", input="Refund my last charge. I do not have an order ID.")

Signature

def kensa_case(
    *,
    id: str,
    input: Any = ...,
    messages: list[KensaMessage] | None = None,
    **fields: Any,
) -> KensaCase
ArgumentRequiredDescription
idYesUnique, stable identifier. Shows up in pytest item ids and verdicts.
inputOne of input/messagesLiteral input passed to your agent.
messagesOne of input/messagesA conversation: a list of role/content messages.
**fieldsNoExtra per-case data (fixtures, expected values, metadata) available on case.row.

Case object

Inside a test, the case fixture is a KensaCase:
Attribute or methodDescription
case.idThe case identifier
case.inputThe resolved input (from input, messages, or the first field)
case.messagesThe conversation list when messages= was provided
case.rowThe immutable mapping of everything passed to kensa_case
case.run(kensa_run)Runs the case through your harness fixture and returns the output

Literal input vs messages

Use input for a single prompt:
kensa_case(id="classify_outage", input="Our entire team can't log in. SSO returns 502 since 7am.")
Use messages when the case is a partial conversation and the agent must respond to the latest turn:
kensa_case(
    id="draft_no_send",
    messages=[
        {"role": "user", "content": "Find the VP of Sales at Acme."},
        {"role": "assistant", "content": "I found Dana Lee."},
        {"role": "user", "content": "Draft a short note to Dana, but do not send it."},
    ],
)
Messages follow the standard role/content shape (system, developer, user, assistant, tool), including tool_calls on assistant turns and tool_call_id on tool turns.

Parametrize multiple cases

One test can cover many cases. Each becomes its own pytest item:
import pytest

from kensa.pytest import judge, kensa_case


@pytest.mark.parametrize(
    "case",
    [
        kensa_case(id="p1_multi_user_outage", input="Whole team locked out since 7am."),
        kensa_case(id="p3_single_user_typo", input="I mistyped my password once."),
    ],
)
def test_severity(case, kensa_run, kensa_trace):
    output = case.run(kensa_run)
    result = judge(output, "Severity must reflect business impact, not tone.", input=case.input)
    assert result.passed, result.reasoning

Trials

Wrap a test with the Kensa marker to run each case more than once:
@pytest.mark.kensa(trials=5)
@pytest.mark.parametrize("case", [kensa_case(id="draft_no_send", input="...")])
def test_sdr_draft(case, kensa_run, kensa_trace):
    ...
Each case expands into one pytest item per trial:
test_sdr_draft[draft_no_send-trial1]
test_sdr_draft[draft_no_send-trial2]
Kensa aggregates the trials into a single verdict per case. trials: 1 is reported as a smoke check; trials > 1 is measured evidence of flakiness. See Pytest plugin for verdict aggregation.

Generated cases

When your coding agent mines imported traces into eval ideas (the kensa-inspect skill) and materializes the approved ones (the kensa-generate skill), the generated tests/evals/test_<id>.py files use this same kensa_case API — plain pytest files you can edit, rename, and extend by hand. See Tracing & imports.
Last modified on July 7, 2026