Skip to content

pytest-torch

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.

pip install pytest-torch

Or try it on any model first, with no test file:

pytest --torch-model=hf:google-bert/bert-base-uncased     # or timm:NAME, torchvision:NAME, mypackage.models:Net

See test any model with no code.

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

Go further: inference correctness (contract, invariance, padding and masks), output specifications, devices, dtypes and unusual inputs.

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), stateful and streaming models.

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, checkpoints, performance and determinism, failure reproduction.

Or generate the tests

ModelSuite turns one class into a test suite: pick the checks (contract, compile, export, output spec, checkpoint, training, ...), and each becomes a test on every device. The helpers it uses stay available on their own.

Requirements and stability

Python 3.9+, pytest 7+ and PyTorch 2.0+. torch.export checks skip on torch 2.0 and work best on recent versions (CI exercises them from 2.2); AOTInductor needs 2.6+. From 1.0, pytest-torch follows semantic versioning: breaking changes to the public API (the names exported from pytest_torch, the fixtures, markers, command-line options and configuration) only come with a new major version.