Computing Library › Verification Validation
Verification Validation

Unit Testing Scientific Code

Small, fast tests on individual functions catch bugs at the source, where they are cheap to find and fix.

Testing the Pieces

A unit test checks a single function or component in isolation against a known expected result. In scientific code, a unit might be a flux calculation, an equation-of-state lookup, or a coordinate transform. Testing units individually catches bugs at the smallest scale, where the cause is obvious, instead of waiting for a wrong answer to emerge from the full calculation where the cause is buried.

What Makes a Good Unit Test

Kronos motion — fast proton

Testing Numerical Functions

Numerical units need care. Because floating-point results rarely match exactly, comparisons use a tolerance. Good tests include not just typical inputs but edge cases: zero, negative values, very large and very small magnitudes, and inputs at the boundary of the valid domain. A physics function should also be tested against any exact values it must reproduce, such as known limits or conservation identities.

python
import math

def test_ideal_gas_pressure():
    # p = n k T ; check against a hand-computed value
    n, k, T = 1.0e20, 1.380649e-23, 1000.0
    p = n * k * T
    assert math.isclose(p, 1380.649, rel_tol=1e-9)

Where Unit Tests Stop

Unit tests verify components, not their integration. A code with every unit tested can still be wrong if the units are wired together incorrectly or if the coupled physics behaves unexpectedly. That is why unit tests sit at the base of a layered strategy, beneath integration tests and full verification cases. Together they give both the fast feedback of small tests and the physical coverage of whole-problem tests.

The payoff is compounding: a unit test written once runs forever, guarding its function against every future change at almost no ongoing cost.