This release is a pre-release and may not be stable for production use.
Test PyTorch models like you mean it. pytest-torch is a pytest plugin with small,
composable helpers for the tests every model needs, and failure messages that say where a
mismatch is and how large it is.
Status: alpha. The API may still change. Requires Python 3.9+, pytest 7+ and PyTorch 2.0+. Documentation: https://akj7.github.io/pytest-torch/
pip install pytest-torch
Start here
Pick the question you're asking. Each path is one or two calls; every snippet is runnable in
examples/test_start_here.py.
1. Check one prediction
Does the model behave like an inference model, and do its outputs look right?
from pytest_torch import Probabilities, TensorSpec, assert_output_spec, check_inference_contract
SPEC = {"logits": TensorSpec(("B", 3)), "probs": Probabilities(("B", 3))}
def test_one_prediction(model, make_tensor):
x = make_tensor(8, 16)
check_inference_contract(model, x) # eval mode, finite, deterministic, batch-invariant, devices
with torch.inference_mode():
assert_output_spec(model(x), SPEC, B=8) # shapes, dtypes, ranges, probabilities
model is your fixture; make_tensor and torch_device come with the plugin, so the test
runs on every available device with seeded inputs. Go further:
invariance (padding, masks,
permutations), output specs,
unusual inputs and dtypes.
2. Check a model change
Does the refactored, ported, compiled or compressed model still match the original?
from pytest_torch import assert_matches_reference, assert_models_agree, localize_divergence
def test_refactor_matches_the_original(original, refactored, make_tensor):
x = make_tensor(8, 16)
assert_matches_reference(refactored, original, x) # same outputs, path by path
localize_divergence(original, refactored, x).raise_if_diverged() # ...and layer by layer
def test_bf16_model_stays_close(original, make_tensor):
x = make_tensor(256, 16)
bf16 = copy.deepcopy(original).to(torch.bfloat16)
assert_models_agree(original, bf16, x, inputs_candidate=x.to(torch.bfloat16), min_top1=0.95)
Go further: deployment parity
(torch.compile, torch.export, AOTInductor, ONNX, autocast),
streaming and stateful models,
tolerances.
3. Check a release
Is the model we're about to ship as good, as compatible and as fast as the last one?
from pytest_torch import assert_checkpoint_loads
def accuracy(output, batch):
return (output["logits"].argmax(-1) == batch[1]).float() # per sample: paired comparisons
def test_release_accuracy(model, val_loader, metric_baseline):
metric_baseline(model, val_loader, {"accuracy": accuracy}, minimum={"accuracy": 0.9},
context={"dataset": "synthetic", "split": "validation"})
def test_last_release_checkpoint_loads():
assert_checkpoint_loads(Classifier, "tests/data/classifier_v1.pt")
def test_speed(model, make_tensor, perf_baseline):
perf_baseline(model, make_tensor(32, 16))
Record baselines once with --torch-update-metrics and --torch-update-perf, commit them,
and CI compares against them. Go further:
dataset metrics (paired
comparisons with confidence intervals),
checkpoints,
performance,
failure reproduction.
Or generate the tests
ModelSuite turns one class into a test suite: pick the checks, and each becomes a test on
every device.
from pytest_torch import ModelSuite
class TestClassifier(ModelSuite):
checks = ("contract", "compile", "export", "output_spec", "checkpoint", "training")
output_spec = {"logits": TensorSpec(("B", 3))}
def make_model(self):
return Classifier()
def make_inputs(self, make_tensor):
return make_tensor(8, 16)
def loss(self, output, inputs): # only needed for "training"
return output["logits"].logsumexp(-1).mean()
See generated test suites for every check and option. The helpers it uses stay available on their own.
Everything else
| Topic | Guide |
|---|---|
| Fixtures: devices, seeds, reproducible tensors, golden files | Fixtures |
Project-wide tolerances in pyproject.toml |
Tolerances |
| Gradients, learning, overfitting one batch, gradcheck | Training sanity |
| Latency, memory, graph breaks, host syncs, nondeterminism, xdist | Performance and determinism |
| Hypothesis strategies for edge-case inputs | Devices, dtypes and unusual inputs |
| Hugging Face, timm and torchvision models | Recipes |
| Command-line options and markers | Reference |
Installation
pip install pytest-torch # or: uv add --dev pytest-torch
PyTorch is not installed for you (pick the build that matches your hardware). The plugin
registers itself through pytest's pytest11 entry point, and importing it never imports
torch. Optional extras:
| Extra | Adds | Needed for |
|---|---|---|
hypothesis |
hypothesis |
pytest_torch.strategies |
onnx |
onnx, onnxruntime, onnxscript |
assert_onnx_parity |
hf |
transformers |
ModelLoader.hf, assert_generation_equal with HF models |
timm, torchvision |
the respective package | ModelLoader.timm, ModelLoader.torchvision |
plots |
matplotlib |
mismatch heatmaps (--torch-failure-artifacts) |
Examples
Runnable test files in examples/ (pytest examples/ -v). Most have a "Try ..."
comment: a one-line bug that makes the test fail.
| Topic | Files |
|---|---|
| Start with | test_start_here.py (the three paths above), test_model_suite.py, test_markers.py |
| One prediction | test_output_specs.py, test_metamorphic.py, test_unusual_inputs.py, test_dtype_matrix.py, test_property_based.py |
| Model changes | test_reimplementation.py, test_deployment.py, test_streaming.py |
| Releases | test_dataset_metrics.py, test_checkpoints.py, test_failure_repro.py, test_tiny_classifier.py |
| More | test_training_sanity.py, test_nondeterminism.py |
examples/integration/ has three small but realistic projects, laid
out like real repositories and tested end to end: a transformer
text classifier, an
image classifier with its deployment pipeline,
and a small GPT with a KV cache.
Development
uv sync # environment, lockfile, dev dependencies
uv run pytest # the plugin's own tests
uv run pytest examples/ -v # runnable examples of every feature
uv run pytest -m "not slow" # skip AOTInductor compilation and other slow tests
uv run --group docs mkdocs serve # documentation at http://127.0.0.1:8000
uv build # sdist and wheel
CI (.github/workflows/ci.yml) runs the tests and the examples over operating systems, Python
versions and torch versions with CPU-only wheels, plus the slow tests once on the newest torch.
docs.yml builds the docs on every PR and publishes them from main; release.yml publishes
to PyPI from a GitHub release.
License
Metadata
Release files for pytest-torch 0.1.0.dev0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pytest_torch-0.1.0.dev0.tar.gz | 76.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pytest_torch-0.1.0.dev0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 167.9 kB
Release files / pytest_torch-0.1.0.dev0.tar.gz
| Download URL | pytest_torch-0.1.0.dev0.tar.gz |
|---|---|
| Size | 76.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
58318b278392bad353771d9ad25f133f0a1d4c6f5c39599bdd9f17c8437d50d0
|
|
BLAKE2b-256 checksum How to use checksums |
464b8c39607c36ae5fa9d0325b0956cd4f780db5e2627211e056d607fa57f2ca
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.26 {"installer":{"name":"uv","version":"0.11.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / pytest_torch-0.1.0.dev0-py3-none-any.whl
| Download URL | pytest_torch-0.1.0.dev0-py3-none-any.whl |
|---|---|
| Size | 91.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6ad312245bee19aa330f30882a998368ba5ebe6e0bb21908fdacdf5a7556be86
|
|
BLAKE2b-256 checksum How to use checksums |
9f7ef94a67544a84d274deca89f640d3d02d0fd1b4489b40143fedd9745e8921
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.26 {"installer":{"name":"uv","version":"0.11.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|