Plain values
Strings, numbers, dictionaries, and lists use equality. This is the simplest option for stable API responses.
returns={"id": 1, "name": "Alice"}Type-only checks
Pass a class as returns to use isinstance. This is useful when IDs or other fields change every run.
returns=UserData # isinstanceで確認Custom conditions
Pass a callable to validate the actual result. Truthy means success, which works well for UUIDs, timestamps, and random values.
returns=lambda result: result["count"] > 0Expected exceptions
Use raises for the exception type and optionally apply a regular expression to its message with match. Synchronous and asynchronous functions are supported.
Exactly one of returns and raises is required. Exception specifications have no fixed value, so they always call the real implementation even in mock mode.
@docs(case(
"残高不足",
given={"balance": 100, "amount": 150},
raises=ValueError,
match="残高不足",
))Dataclasses and Pydantic
Dataclasses and Pydantic models are compared by fields, with expected and actual fields shown on failure.
Pydantic type conformance
conforms_to() uses TypeAdapter to validate models, collections, unions, and Annotated constraints. Install niltest[pydantic] to use it.
from niltest import conforms_to
returns=conforms_to(list[User])
returns=conforms_to(Annotated[int, Field(gt=0)])Case input validation
Before a case runs, niltest checks argument names, required arguments, and annotated input types. Invalid cases are reported without calling the implementation, while dictionaries can be normalized into annotated Pydantic models.
Normalization applies only while run_tests() or the CLI executes specification cases. niltest never transforms normal application calls or production-mode inputs.
class Payload(BaseModel):
count: int
@scenario("集計")
@docs(case("辞書入力", given={"payload": {"count": 2}}, returns=4))
def process(payload: Payload) -> int:
# case実行時、payloadは検証済みのPayloadモデル
return payload.count * 2