Skip to content
AI.info

MLOps

Testing Machine Learning Systems

Build a layered testing strategy for code, data, models, pipelines, services, policies, and recovery behavior.

By the end you can

The unit tests pass because the contract is not in them

A service can have excellent code coverage and still hand another component a number in the wrong unit. The Mars Climate Orbiter was lost on 23 September 1999. The Mishap Investigation Board's Phase I Report, published on 10 November 1999, put the cause in one sentence: “The MCO MIB has determined that the root cause for the loss of the MCO spacecraft was the failure to use metric units in the coding of a ground software file, “Small Forces,” used in trajectory models.” Thruster impulse data was delivered in pound-seconds. The Software Interface Specification required newton-seconds. The specification existed and named the unit; nothing about it was ambiguous. The navigation software underestimated the trajectory effect by the conversion factor 4.45, and the spacecraft arrived roughly 170 km lower than planned. The US General Accounting Office described the same units error in 2002.

Every function in that chain could have passed its own unit test. What no test asserted was the agreement between two components about what a number meant. ML systems inherit that failure mode and add several of their own, because a learned component also has a data contract, an output contract and a distribution it was fitted on. They need ordinary software testing plus tests for distributions, invariants, statistical tolerance, workflow, and recovery. Exact expected predictions are sometimes useful. They are not the whole strategy.

Visual

A testing pyramid for ML systems

Fast local tests provide breadth. Fewer integrated tests cover the seams where operational failures hide.

The layers are not a matter of taste, and someone has already counted them. Breck and four colleagues at Google published a rubric in 2017, and its abstract promises exactly that: “we present 28 specific tests and monitoring needs”. They come grouped in four areas — features and data, model development, ML infrastructure, and monitoring. The scoring is deliberately unforgiving. Half a point for a test performed manually, a full point for one that runs automatically. The final score is the minimum across the four sections, so strength in one area cannot pay for a weak one. The authors met with 36 teams from across Google, and reported what they found: “in a survey of several dozen teams at Google, none of these tests was implemented by more than 80% of teams”. A pyramid nobody fills in completely is the normal case, not the failing one. Naming the layers tells you which one you have chosen to leave empty.

FigureHierarchy · 5 levels
  • Unit and property tests

    Pure transformations, schema utilities, metric code, and deterministic invariants.

    • Data and model tests

      Quality rules, distribution checks, tiny-set behavior, and contract examples.

      • Pipeline and integration tests

        Artifact handoffs, lineage, retries, registry interactions, and service dependencies.

        • System and shadow tests

          End-to-end behavior under representative traffic without changing user outcomes.

          • Recovery and game-day tests

            Rollback, degraded dependencies, delayed labels, and operator response.

Comparison

Three ways to test learned behavior

Each method detects a different class of failure.

Metamorphic testing is not a fallback for when labels are missing; it finds things labels do not. In 2018 a tool called DeepTest generated inputs with nine realistic image transformations and built its oracle out of metamorphic relations rather than labels. It ran at three top-performing models from the Udacity self-driving car challenge. The results section is blunt: “In total, DeepTest detects 6339 erroneous behaviors across all three models.” Manual review judged 130 of those cases false positives. The models had already scored well on the challenge. That is the only reason the number is interesting.

Statistical tests are where a vendor's held-out metric stops counting as evidence about your population. Wong and colleagues took the proprietary Epic Sepsis Model and validated it at Michigan Medicine, on 27,697 patients and 38,455 hospitalizations. They measured an AUC of 0.63 (95% CI 0.62–0.64), against the 0.76–0.83 range the developer had reported. The results section of their abstract gives the operational shape of that gap: “The ESM did not identify 1709 patients with sepsis (67%) despite generating alerts for an ESM score of 6 or higher for 6971 of all 38 455 hospitalized patients (18%), thus creating a large burden of alert fatigue.” Their conclusion, in JAMA Internal Medicine in 2021, was that “the ESM has poor discrimination and calibration in predicting the onset of sepsis”. A separate team then evaluated the same vendor model across 145,885 encounters in two county emergency departments during 2023, and measured 14.7% sensitivity, 95.3% specificity and 7.6% PPV. Two independent measurements, two settings, the same verdict about a number that had shipped as a specification.

FigureComparison · 3 columns

Golden examples

Assert expected behavior on carefully chosen cases.

  • Useful for critical regressions
  • Human-inspectable
  • Can become narrow or stale
  • Example: known fraud pattern must exceed review threshold

Metamorphic tests

Transform an input and assert a relation between outputs.

  • Works without exact labels
  • Tests invariance or sensitivity
  • Requires a justified relation
  • Example: reordering irrelevant fields should not change a score

Statistical tests

Compare distributions or metrics within declared tolerances.

  • Handles stochastic variation
  • Supports slices and batches
  • Needs power and multiple-testing care
  • Example: calibration error must remain within a confidence interval

Example

Twelve automated tests, all wildcards, and a field count nobody asserted

CrowdStrike published its own root cause analysis on 6 August 2024, and it describes a test suite that was green for a reason unrelated to correctness. The “What Happened” section states: “In summary, it was the confluence of these issues that resulted in a system crash: the mismatch between the 21 inputs validated by the Content Validator versus the 20 provided to the Content Interpreter, the latent out-of-bounds read issue in the Content Interpreter, and the lack of a specific test for non-wildcard matching criteria in the 21st field.”

  • The contract: The IPC Template Type for Channel File 291 declared 21 input parameter fields. The sensor's Content Interpreter supplied 20. Nothing in the pipeline asserted that the two numbers had to agree.
  • The suite: All 12 automated test cases used a regex wildcard in the 21st field. That field was never really exercised, and the mismatch stayed invisible to every one of them.
  • The green light: The gap survived build validation, release testing and several successful field deployments — passing evidence that was accumulating about a branch nobody was taking.
  • The trigger: On 19 July 2024 a Template Instance with non-wildcard matching criteria shipped. The 21st field was read, and the out-of-bounds read crashed sensors: about 8.5 million Windows devices, on Microsoft's own estimate, published by David Weston. “We currently estimate that CrowdStrike's update affected 8.5 million Windows devices, or less than one percent of all Windows machines.”
  • The repair: CrowdStrike's stated mitigations are the shape of the lesson: tests with non-wildcard criteria for every field, staged deployment of Template Instances, and runtime array bounds checks. The contract, the blast radius and the failure mode, each addressed separately.

Tests should target stable properties

Model outputs vary across hardware, seeds and library versions while remaining acceptable, and that variation has been measured rather than assumed. Sara Hooker and three colleagues trained ResNet-50 on ImageNet ten times, in 2022, and reported top-1 accuracy of 76.58% ± 0.10. Implementation-level noise alone produced 14.68% predictive churn between runs — roughly one prediction in seven flipping between models that a summary table calls identical. Their abstract names the asymmetry directly: “While top-line metrics such as top-1 accuracy are not noticeably impacted, model performance on certain parts of the data distribution is far more sensitive to the introduction of randomness.” Pham and colleagues found the same effect independently in 2020. Implementation-level factors alone moved accuracy by up to 2.9%, per-class accuracy by up to 52.4% and training time by up to 145.3% across identical training runs. Of the 901 researchers and practitioners they surveyed, 83.8% were unaware of or unsure about that variance.

A test that pins one exact float is asserting a number that moves for reasons the production contract does not care about, while saying nothing about the per-class collapse the same noise can hide. Better targets include schema, monotonicity, invariance, bounded sensitivity, calibration tolerance, slice behavior, resource limits, and fallback semantics. The property should follow from the production contract. A tolerance of ±0.10 on an aggregate is not a tolerance on the slice that matters.

Case

Negation at the end of a sentence broke every commercial model

CheckList tested behaviour instead of held-out accuracy. Ribeiro and colleagues filled a negation template as a Minimum Functionality Test in 2020 and ran it at a commercial sentiment model. Figure 1 of the paper records the outcome as “Failure rate = 76.4%”. Then they moved the negation to the end of the sentence, and it got worse: “the failure rate is near 100% for all commercial models when the negation comes at the end of the sentence”. Accuracy on the benchmark had called those models human-level. That is the whole reason the result is worth remembering. The benchmark and the behavioural test were measuring different things, and only one of them was a contract.

Figure

A commercial sentiment model against one negation template: 76.4% failures, and near 100% once the negation moves to the end.

Analogy

Passing one scale of inspection does not prove the next

Bridges are tested through material samples, component load tests, full-span trials, traffic observation, and emergency drills. Passing one scale does not prove the next. Failures can emerge from interactions.

Traffic over a bridge changes slowly. It stays inside the load the engineer assumed. ML behavior depends on populations and labels that move faster than that, so testing continues after deployment and evolves with the failures actually observed.

Exposure is not coverage. On 18 March 2018, in Tempe, an Uber ATG automated driving system detected a pedestrian 5.6 seconds before impact, on a route the ATG fleet had already driven about 50,000 times. It never classified her as a pedestrian. It alternated between vehicle, bicycle and other, discarded its tracking history at each reclassification, and then suppressed its motion plan for one second. The National Transportation Safety Board, reporting in 2019, explained why: “The system never classified her as a pedestrian—or correctly predicted her path—because she was crossing N. Mill Avenue at a location without a crosswalk, and the system design did not include consideration for jaywalking pedestrians.” Fifty thousand traversals of that road exercised the system heavily and never once tested the class of input that killed someone.

Testing depth should follow the system boundary where a failure becomes visible.

Key idea

Shadow traffic is not consequence-free

A shadow deployment may not affect user decisions, but it still reads data, consumes capacity, emits logs, and can call downstream tools if nobody has isolated it properly. It may also expose sensitive inputs to a candidate environment.

Define the shadow boundary, scrub side effects, apply access controls, and monitor resource contention.

“No user-facing action” does not mean “no operational or privacy risk.”

Steps

Design tests from the production contract

Translate each clause into the cheapest test that can detect a meaningful violation.

Three regulators have already published a version of this exercise. In October 2021 the US FDA, Health Canada and the UK MHRA jointly issued 10 guiding principles for Good Machine Learning Practice for Medical Device Development. Principle 4 is “Training Data Sets Are Independent of Test Sets”. Principle 8 states: “Testing Demonstrates Device Performance During Clinically Relevant Conditions: Statistically sound test plans are developed and executed to generate clinically relevant device performance information independently of the training data set.” Principle 10 carries the obligation past release, to monitoring deployed models.

Read as a contract-to-test mapping, that is the five steps below, with an issuing agency behind each one. Independence of the test set is a clause you can violate silently. Clinically relevant conditions are the deployment population, not the convenient one. Principle 10 is the admission that release testing expires. A team outside medical devices is not exempt from the logic — only from the audit.

FigureProcess · 5 steps
  1. 1. List contracts and invariants

    Cover data meaning, output semantics, service limits, and controls.

  2. 2. Choose the test layer

    Use unit, integration, shadow, or game-day tests according to the failure boundary.

  3. 3. Define tolerances

    Separate harmless numerical variation from behavior-changing regression.

  4. 4. Add negative and degraded cases

    Test missing features, stale data, timeouts, malformed requests, and policy denial.

  5. 5. Verify recovery

    Exercise rollback, fallback, reconciliation, and operator runbooks.

A failing test should identify a violated contract

Tests are easiest to maintain when their names state the property and the consequence. “Prediction changed” is weaker than “threshold decision changed for a protected fixture under the approved policy.”

Review the suite after incidents, and write the general mechanism rather than the escaped example. CrowdStrike's own mitigation list is the template. Not a test for the Template Instance that shipped on 19 July 2024, but tests with non-wildcard criteria for every field, runtime array bounds checks, and staged deployment — the contract, the failure mode and the blast radius, each given its own test. A regression test that only reproduces the one input which escaped is a test of history.

Case

Twice the tests and three times the bugs, from the same practitioners

The CheckList authors measured whether their method changes what a team finds, rather than asserting it. Their abstract reports the user study: “NLP practitioners with CheckList created twice as many tests, and found almost three times as many bugs as users without it”. The same people, the same models, a different way of deciding what to check. The bottleneck was ideation, and the study is the reason to believe that rather than assume it. Knowing which property to test is harder than writing the assertion.

Key takeaways