Core concepts¶
Everything in modeltest revolves around four pieces:
ModelSuite ──run(model, X_val, y_val)──▶ TestContext ──▶ [ModelTest, ...] ──▶ SuiteResult
- A
ModelSuiteholds a list of tests and knows how to run them against a model plus validation data. - Running a suite builds a
TestContext— everything a test may need: the model,X_val/y_val, optional train data, metadata, and a shared prediction cache. - Each
ModelTestreceives the context and either passes (returns normally after itsasserts) or fails (raisesAssertionError). An unexpected exception marks the test as ERROR, not FAILED. - The suite collects every test's
TestResultinto aSuiteResult, which aggregates pass/fail counts and renders reports.
Test statuses¶
| Status | Meaning |
|---|---|
PASSED |
The test returned normally — every assert held. |
FAILED |
An AssertionError was raised — the contract is violated. |
ERROR |
Something else went wrong (bad config, missing column...). |
SKIPPED |
The test decided not to run (returned a TestResult with this status). |
Only PASSED counts towards the suite passing: SuiteResult.passed is
True when every result passed.
Prediction caching¶
Within a single suite.run(...), predictions are computed once and
reused across every test. TestContext.predict() caches by a content hash
of the input, so tests predicting on the same data (accuracy, group
performance, the fairness tests...) each reuse the result instead of
re-running the model.
Perturbed inputs get their own cache entry, so the robustness test's noisy copy never collides with the clean data — caching never compromises correctness.
Disable it if you need a fresh prediction on every call:
from modeltest import TestContext
ctx = TestContext(model=model, X_val=X_val, y_val=y_val, cache_predictions=False)
Feature-name filtering¶
If the model was fit with named features (feature_names_in_, true for
sklearn estimators and Pipelines), TestContext.predict() drops any extra
columns from X_val that the model does not expect, in the model's own
order. This lets your validation frame carry helper columns (a group column,
timestamps...) without breaking the model call.
Reports¶
SuiteResult.report() (or result.report(style=...)) renders three ways:
result.report(style="table") # console table (default)
result.report(style="json") # machine-readable JSON
result.report(style="junit") # JUnit XML for CI test reporters
The JUnit output wraps everything in a standard <testsuites> element, which
is what GitHub Actions, GitLab and Jenkins reporters expect. Exit codes:
modeltest validate returns 0 when the suite passes and 1 when any test
fails, so plain if semantics work in shell scripts and CI.
Metadata¶
Any extra keyword arguments to suite.run(...) land in
ctx.metadata — model_name="fraud_rf" shows up in reports and MLflow.
result = suite.run(model, X_val, y_val, model_name="fraud_rf", version="2.1")