Skip to content

Regression test (software)

A repeatable check that protects existing software behaviour after changes, using a retained valuation bug fixture and an independent expected result.

A regression test is a repeatable check that existing software behaviour still meets its requirements after a change. In quant work, it can reveal that a refactor, dependency update or bug fix has changed a valuation, risk calculation or data transformation that previously worked. The name refers to software getting worse after a change; it does not mean fitting a statistical model such as linear regression. NIST's definition focuses on protecting desired functionality from unacceptable changes.
“Regression” describes the test's purpose. A unit test of one calculation and an integration test of connected components can both serve as regression tests. A test need not originate from a past defect: a retained check for an already working feature also protects against regressions.

Turn a bug into a lasting check

Consider an invented software history, with fictional financial inputs. A function should value 2 whole shares at USD 100 per share. Its contract is quantity multiplied by price, giving USD 200. An earlier bug used addition and returned the raw number 102. Adding shares to dollars per share is dimensionally invalid: 102 is not a valid dollar valuation.
After repairing multiplication, retain a test fixture containing the inputs and the independently justified expected result. Here the oracle—the rule for deciding correctness—is the value of two separately held shares: USD 100 plus USD 100. Do not obtain the expected value by calling the same valuation function being tested.
Fictional regression history for 2 shares at USD 100 per share: an addition bug returns 102 with invalid units and fails; multiplication returns USD 200 and passes; a later reversion returns 102 and fails against the same USD 200 oracle.
Fictional regression history for 2 shares at USD 100 per share: an addition bug returns 102 with invalid units and fails; multiplication returns USD 200 and passes; a later reversion returns 102 and fails against the same USD 200 oracle.
The expected value stays fixed across versions. Reintroducing the original defect makes the retained test fail again; the fictional buggy output 102 has no valid valuation units.
This self-contained Python example uses the standard-library unittest framework. Integer inputs deliberately avoid a floating-point tolerance decision.
import unittest def position_value(quantity: int, price_usd: int) -> int: """Value whole shares at an integer USD price per share.""" return quantity * price_usd class PositionRegression(unittest.TestCase): def test_two_shares_at_100_usd(self): self.assertEqual(position_value(2, 100), 200) result = unittest.TestResult() PositionRegression("test_two_shares_at_100_usd").run(result) print(f"tests={result.testsRun} failures={len(result.failures)} " f"errors={len(result.errors)}") # tests=1 failures=0 errors=0
The summary is a deliberately compact report from TestResult, not the framework's usual command-line display. The test method names the case and checks the result against the fixed oracle. In a project, put the test in the test suite and import the actual implementation; a copied function in a test file could pass while production code remains broken. Python's unittest documentation explains test cases, collection and execution.
As a sensitivity check, replacing only the multiplication with addition makes this same test report tests=1 failures=1 errors=0; the assertion identifies 102 != 200. This deliberate defective variant demonstrates that the check detects the targeted fault. Keep the repaired implementation and retain the test, rerunning it after later changes. The original report confirms the repair; future runs protect the repair.

Preserve the contract, review the baseline

A saved output is useful only when someone has established why it is correct. Freezing a buggy historical output merely preserves a bug. For a numerical model, record the input conventions, source of the reference result and acceptance rule alongside the fixture. With floating-point arithmetic, justify an absolute or relative tolerance instead of widening it until a changed answer passes. For stochastic calculations, retain the seed and relevant environment details; repeatable random draws alone do not establish numerical accuracy.

When requirements deliberately change, review the expected result and its rationale together. Updating a baseline automatically to match the new implementation removes the test's independence. Conversely, insisting on obsolete output can block a correct change.

A passing fixture gives evidence about that case. It does not establish correctness for every position size, currency, invalid input or market scenario, nor validate a trading model's economic merit. Add cases for distinct failure risks, and use property-based testing when a relationship should hold across many generated inputs.
For numerical extensions, Testing Numerical Code develops pricing references, parity checks and tolerances. A public preview is available; the full lesson requires membership.

Sources

Last updated
  • software testing
  • regression testing
  • quant software
← Back to the glossaryBack to top ↑