What a Test Run Actually Records
Store a test run as a list of pointers and the steps drift while the verdict stays put. Hawzu copies the test case into the run and pins its versions — which is what makes execution history mean anything.
Six weeks after a release ships, someone asks what was actually tested.
You open the run. Everything passed. Reassuring — except the test case you’re looking at has been edited four times since: a step added, a precondition rewritten, an expected result corrected after someone realised it was wrong.
So what does that green checkmark mean? It was recorded against a version of the test that no longer exists. The verdict is six weeks old. The steps rendered underneath it are from this morning.
A run is not a list of pointers
The obvious way to store a run is as a set of references: test case IDs and a verdict for each one. Open it, and today’s version of the case gets fetched and rendered beside a result recorded weeks ago.
This is a reasonable-looking design that quietly destroys the thing a run exists to produce. The record and the referent drift apart, and nothing announces it. Nobody edits a test case thinking I am now rewriting the history of every run that used it — but that’s what happens.
A receipt that reprints itself with today’s prices isn’t a receipt.
The assumption underneath is that a test case is one thing with a current state. It isn’t. There’s the case as it stands in your repository — a living document you improve — and there’s the case as it existed at 2:14pm on the Tuesday someone ran it. Only the second one is evidence.
Hawzu copies the case into the run
When a test case is added to a run, Hawzu doesn’t point at it. It takes a copy — steps, expected outputs, preconditions, the lot — and writes that copy into the run as its own record.
Anything the case borrows gets pinned at the same moment. A shared step isn’t recorded as “uses the login sequence”; it’s recorded as “uses version 7 of the login sequence”, and version 7 is kept in full, permanently, alongside every other version any run has ever pulled in. Parameters work the same way. Datasets are copied by content, so a run stays readable after someone edits the data it used.
- Open checkout as a guest
- Apply promo code
- Confirm total inc. tax
- Open checkout as a guest
- Apply promo code
- Confirm total inc. tax
Every consequence people usually want from a test management tool falls out of this one decision rather than being bolted on top of it. Editing a test case can’t rewrite last quarter’s run, because last quarter’s run isn’t reading from the test case. Deleting a case doesn’t gut the runs that exercised it — the evidence lives in the runs, and deletion never touches them. Immutability stops being a policy somebody has to enforce and becomes a property of where the data sits.
A frozen copy goes stale, and that has to be visible
There’s an obvious cost. If the run holds a copy and the source keeps moving, the run is out of date the moment someone improves the case — and a tester working from a stale copy is testing the wrong thing.
So the run watches for drift. A newer version of a shared step, a newer parameter, a changed dataset, an edited step: the run compares what it froze against what the repository holds now and grades the difference as major or minor. Major drift raises a badge on the case inside the run.
What it does not do is update itself. Bringing the changes in is an explicit action, and before the tester takes it they see a step-by-step diff of what would change. The diff aligns steps by identity rather than position, which matters more than it sounds: if step 3 moves to step 6, a position-based comparison reports two edits — one step deleted, one added — and buries the fact that nothing about the step changed at all. Identity-based alignment reports it as a move.
What stays mutable, and why
Being precise about this matters, because “immutable execution” is easy to overclaim.
While a run is open, results move. A case goes from Not Executed to Failed to Blocked to Passed as a tester works, and that’s the entire point — a run in progress is a working surface, not an archive.
What’s immutable is the record of what ran. The frozen copy of the case doesn’t change underneath the verdict, and the intermediate verdicts aren’t overwritten: each status change is appended to a history on the row, which is the only place the path from Failed to Passed survives. The current status tells you where a case ended up. The history tells you what it took to get there, and those are different questions.
There’s one more line worth drawing. Some statuses are yours to set — Not Executed, Passed, Failed, Blocked, Skipped. Others belong to the system: when a case needs rerunning because its source drifted, only Hawzu can say so. A tester can’t type their way into a system verdict, and the system won’t overwrite a human one.
Why this is worth the storage
Copying is more expensive than pointing. It’s more rows, more indexes, and every version of every shared step kept forever.
It buys the only thing that makes execution history worth keeping. A run you can reopen in six months and read exactly as the tester read it — the steps they followed, the data they used, the expectation they checked against — is evidence. A run that renders today’s test cases beside last quarter’s verdicts is a decoration that looks like evidence, which is worse than not having one.
It also decides what you can say about a cycle while it’s still running — the difference between a completion percentage and knowing what’s left, who holds it and what’s stuck.
This is what managing test cases was never going to give you on its own. A tidy repository describes what you intend to test. Only the run describes what happened.
A test run isn’t a pointer to your test cases. It’s a record of what you ran.
What changes when a run completes