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.
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
@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
niltest.configure(mode="MOCK")
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.
Production path
Set PRODUCTION=true before importing decorated modules. In this 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 PRODUCTION=true, 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.
日本語
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file niltest-0.1.0.tar.gz.
File metadata
- Download URL: niltest-0.1.0.tar.gz
- Upload date:
- Size: 26.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c7abd20f06f8d59cedddc3784df28709ee34a693ef48745f057eef36bf3ecee2
|
|
| MD5 |
30637945f2d2a97df20966aec34f7307
|
|
| BLAKE2b-256 |
d690fbf6ce63c73c62006424de0d3c46e2e2cb1c83d72591c2103805e2bb4d99
|
Provenance
The following attestation bundles were made for niltest-0.1.0.tar.gz:
Publisher:
ci.yml on disnana/niltest
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
niltest-0.1.0.tar.gz -
Subject digest:
c7abd20f06f8d59cedddc3784df28709ee34a693ef48745f057eef36bf3ecee2 - Sigstore transparency entry: 2181709726
- Sigstore integration time:
-
Permalink:
disnana/niltest@4902d6da7608020763e41599d88459d586a64bc2 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/disnana
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@4902d6da7608020763e41599d88459d586a64bc2 -
Trigger Event:
push
-
Statement type:
File details
Details for the file niltest-0.1.0-py3-none-any.whl.
File metadata
- Download URL: niltest-0.1.0-py3-none-any.whl
- Upload date:
- Size: 24.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ef649e29d4247dac339c35ee28266dc326c7d9c6d75d8411a4958e65d4d2d489
|
|
| MD5 |
d1dd082b909031ee70f8c65e538f9c9a
|
|
| BLAKE2b-256 |
e339f2990088682f7dca08e7b136ac00cf9dac292d778168cf447f22fa91a470
|
Provenance
The following attestation bundles were made for niltest-0.1.0-py3-none-any.whl:
Publisher:
ci.yml on disnana/niltest
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
niltest-0.1.0-py3-none-any.whl -
Subject digest:
ef649e29d4247dac339c35ee28266dc326c7d9c6d75d8411a4958e65d4d2d489 - Sigstore transparency entry: 2181710054
- Sigstore integration time:
-
Permalink:
disnana/niltest@4902d6da7608020763e41599d88459d586a64bc2 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/disnana
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@4902d6da7608020763e41599d88459d586a64bc2 -
Trigger Event:
push
-
Statement type: