Skip to main content

Executable specifications, mocks, and lightweight tests next to your Python code

Project description

niltest

Executable specifications, fixed development mocks, and lightweight checks—next to the Python function they describe.

日本語 · Documentation · GitHub · Issues

niltest lets one expect.case() definition serve three purposes:

  • readable behavior close to the implementation;
  • a fixed mock when databases or external APIs are unavailable;
  • an executable check against the real function.

It has no runtime dependencies and supports Python 3.10+ on Windows, macOS, and Linux. Pydantic-backed type validation is available through the optional niltest[pydantic] extra.

Install

# Active virtual environment (Windows, macOS, or Linux)
pip install niltest

# Windows
python -m pip install niltest

# Ubuntu / macOS
python3 -m pip install niltest

A complete example

import niltest
from niltest import expect, scenario

niltest.configure(mode="mock")  # Configure before @scenario is evaluated.

@scenario("Shipping fee")
def shipping_fee(subtotal: int, premium: bool = False) -> int:
    if expect:
        expect.case(
            "Premium members ship free",
            given={"subtotal": 1_000, "premium": True},
            returns=0,
        )
        expect.case(
            "Standard shipping",
            given={"subtotal": 1_000, "premium": False},
            returns=500,
        )

    return 0 if premium or subtotal >= 5_000 else 500


assert shipping_fee(1_000, premium=True) == 0

niltest.configure(mode="test")
result = niltest.run_tests(shipping_fee)
assert result.success

CLI and CI

niltest run your_package.specs --language en
niltest run your_package.specs --json

The CLI exits with 0 when all cases pass, 1 for specification failures, and 2 for usage or import errors. JSON output and the structured RunResult API make niltest easy to compose with CI and other developer tools.

Expectations

returns supports:

  • plain values and collections;
  • dataclass instances;
  • Pydantic models;
  • type-only checks such as returns=UserData;
  • custom validators such as returns=lambda result: result["count"] > 0;
  • synchronous and asynchronous functions.

Typed specifications and inspection

Version 1.1 adds conforms_to(), backed by Pydantic's TypeAdapter. It validates models and arbitrary annotations such as list[User], unions, and Annotated constraints. During specification execution, case inputs are checked against the function signature and annotations, then normalized (for example, a dictionary can become a Pydantic model) before the implementation runs. Normal application calls and production mode inputs are never transformed by niltest.

from niltest import case, conforms_to, docs, scenario

@scenario("User lookup")
@docs(case("existing", given={"user_id": 1}, returns=conforms_to(User)))
def fetch_user(user_id: int) -> dict[str, object]:
    return {"id": user_id, "name": "Alice"}

Use niltest inspect your_package.services as a compact architecture map, or add --json to feed the same information to other tools.

Production path

NILTEST_MODE=production is the safe default. Set NILTEST_MODE=test or NILTEST_MODE=mock before importing decorated modules when you want niltest's development features. In production mode, @scenario returns the original function without creating a wrapper. The explicit if expect: truth check remains; niltest documents this measurable cost instead of claiming absolute zero overhead.

For zero niltest cost at function-call time, use the declaration-style API. The existing inline API remains fully supported.

from niltest import case, docs, scenario

@scenario("Shipping fee")
@docs(case("Premium", given={"premium": True}, returns=0))
def shipping_fee(premium: bool) -> int:
    return 0 if premium else 500

With the default NILTEST_MODE=production, this returns the original function: no wrapper and no if expect: branch in its body.

Localization

Japanese and English are built in. niltest detects the OS locale, falls back to English, and exposes register_locale() plus a validated template for additional languages.

Verifying release provenance

Official wheel and source distributions are published through PyPI Trusted Publishing and carry Sigstore digital attestations. GitHub Releases contain the same files with GitHub build-provenance attestations tied to the source commit and CI workflow. An isolated, trusted SLSA generator also produces non-forgeable SLSA Build Level 3 provenance for every distribution.

After downloading a release asset, verify its origin with the GitHub CLI:

gh attestation verify niltest-*.whl --repo disnana/niltest
gh attestation verify niltest-*.tar.gz --repo disnana/niltest

For strict SLSA verification, download the matching .intoto.jsonl file from the GitHub Release and use slsa-verifier:

slsa-verifier verify-artifact niltest-*.whl \
  --provenance-path multiple.intoto.jsonl \
  --source-uri github.com/disnana/niltest \
  --source-branch main

Verification confirms provenance; it does not replace reviewing the package or its dependencies for your use case.

日本語

niltestは、Python関数の先頭に代表的な入力と期待値を書くことで、同じ定義を「読める仕様」「開発用モック」「実装チェック」に再利用する軽量ライブラリです。

python -m pip install niltest
niltest run your_package.specs --language ja

日本語の詳しい使い方、モック・非同期関数・バリデータ・本番モード・応用例は公式ドキュメントで確認できます。

MIT © 2026 Disnana

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

niltest-1.1.0.tar.gz (32.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

niltest-1.1.0-py3-none-any.whl (29.4 kB view details)

Uploaded Python 3

File details

Details for the file niltest-1.1.0.tar.gz.

File metadata

  • Download URL: niltest-1.1.0.tar.gz
  • Upload date:
  • Size: 32.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for niltest-1.1.0.tar.gz
Algorithm Hash digest
SHA256 2941d7cc008fe93f1162d0a187eb1fec3586ab6d273821cbd0306e54633e9bdc
MD5 85cd5f841e50e9eda8f8512f3449b6bf
BLAKE2b-256 447326f67b5114e9b173b2be98091500972f99395d614dc1a012db9eaeb55ef9

See more details on using hashes here.

Provenance

The following attestation bundles were made for niltest-1.1.0.tar.gz:

Publisher: ci.yml on disnana/niltest

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file niltest-1.1.0-py3-none-any.whl.

File metadata

  • Download URL: niltest-1.1.0-py3-none-any.whl
  • Upload date:
  • Size: 29.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for niltest-1.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 dc670abe004286fbdbb150e8dde10f83a54ad9d7735a4f36771e2baa2aad0d7e
MD5 ed04215f519ed3943fabf01cbce55d02
BLAKE2b-256 7a9f6a4d510fdcdc61f20d4cdce4358204f10392b92e76b34bc19e6d2951626e

See more details on using hashes here.

Provenance

The following attestation bundles were made for niltest-1.1.0-py3-none-any.whl:

Publisher: ci.yml on disnana/niltest

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page