Safeguard
Developer Tools•

What is a characterization test, and when do you write one?

A characterization test records what code does today so an upgrade can be checked against it. When to write one, how, and what to do with surprises.

Safeguard Security Team
4 min read

A characterization test records what a piece of code actually does today, right or wrong, so you can tell whether an upgrade changed it. Write them before upgrading code that has thin tests and a dependency you are about to change. They answer "did the behavior move?" rather than "is the behavior correct?".

Definition and purpose

Most upgrade guidance assumes you have good tests. Many services do not, especially older ones with a vulnerable library you now need to bump. A characterization test, sometimes called a golden master or snapshot test, fills that gap. You call the code with a range of inputs, capture the outputs as they are, and assert they stay the same after the change.

The key idea is that you are not judging the output. If the old code returns a strange date format, the test records the strange format. After the upgrade, a difference is a signal to look, and then you decide whether the change is a fix or a break.

When it is worth the effort

SituationWrite characterization tests?
Library with strong tests around it alreadyNot needed
Parser, formatter or serializer you are upgradingYes, the output is easy to capture
Legacy module with no tests and a vulnerable dependencyYes, before touching anything
Code with heavy side effects and no clear outputMaybe, capture the calls it makes instead
Code you plan to delete soonNo

Focus on code that calls the package being upgraded. You do not need to characterize the whole application.

How to write them

  1. Find the seam. Identify the function or endpoint closest to the dependency that returns something observable.
  2. Collect inputs. Pull realistic examples from logs, fixtures or support tickets. Remove personal data and secrets first. Add edge cases: empty values, very large values, odd encodings, boundary dates.
  3. Run the old code and save the output. Store inputs and outputs as files in the repository, so a diff is reviewable.
  4. Assert equality. The test replays the inputs and compares with the saved outputs.
  5. Bump the dependency and run the tests.
  6. Triage each difference. Is it a bug fix you want, a harmless change such as ordering or whitespace, or a real break?

For differences you accept, update the saved output in the same pull request and say why. The commit history then explains every behavior change that came with the upgrade.

Handling noise

Naive snapshots fail on timestamps, random identifiers and map ordering. Normalize those before comparing. Freeze the clock and seed randomness in the test setup. A characterization suite that fails for unrelated reasons gets deleted, so keep it deterministic.

What it does not tell you

It shows that behavior is unchanged for the inputs you chose. It says nothing about inputs you did not choose, performance, or security properties. Pair it with a smoke test and a staged rollout. See what a smoke test should check and security regression testing.

Security angle

If you are upgrading because of an advisory, include inputs that exercised the vulnerable behavior. The test should show a difference in exactly that case, from unsafe to safe. If it shows no difference, either the fix did not take effect or your inputs did not reach it, and both are worth knowing.

Safeguard's reachability analysis can show whether your code calls the vulnerable function, which helps you choose inputs that matter. Free scan: gold.safeguard.sh/scan.

Quick check

Is a characterization test the same as a snapshot test?

They are close. A snapshot test saves output and compares later. A characterization test uses the same mechanism with a specific goal: documenting existing behavior before a change.

Should I keep these tests after the upgrade?

Keep the useful ones as regression tests and delete the rest. If the module later gains proper unit tests, retire the snapshots.

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.