On every team people talk about test coverage, CI speed and the right tools. Almost nobody talks about where those tests get their data from. Yet that's often the silent cause behind flaky tests.
Yet that's often the silent cause behind tests that fail at random, bugs nobody can reproduce, and a suite the team is slowly losing faith in. Test data deserves as much attention as the tests themselves, and in practice it almost never gets that attention.
Tests that pass or fail at random because of inconsistent or shared test data. There's no bug in the test code, the bug is in the data the test runs on.
A bug you can't reproduce because the test data has since changed costs more time than it will ever save. You end up hunting for a fault that appears to have vanished, when really only the data underneath changed.
Both problems feel technical, but the cause is almost always organisational: tests share data with each other, or with an environment other people also use. Once that happens, it's only a matter of time before the suite becomes unreliable.
The fix sounds simpler than it is to apply structurally: treat test data as code. Reproducible, isolated per test, and built through factories or isolated fixtures instead of a shared database everyone edits.
In practice this means every test builds its own data at the start, and uses nothing another test might have changed. That feels like extra work at first. It pays for itself quickly once you notice how much time currently goes to figuring out whether a failing test is a real bug, or just data that no longer matches.
A factory like this doesn't need to be complex — the point is that every test gets its own, predictable data:
// every call creates a fresh, isolated user
function createUser(overrides = {}) {
return {
id: crypto.randomUUID(),
email: `test-${Date.now()}@qadvice.be`,
role: 'customer',
...overrides
};
}
// usage in the test: explicit about what matters, defaults for the rest
test('an admin sees the admin panel', async () => {
const admin = createUser({ role: 'admin' });
await loginAs(admin);
await expect(page.locator('#admin-panel')).toBeVisible();
});
No shared database, no cleanup script that can fail, no test failing because a colleague was testing with the same test user yesterday. Every test brings its own data and leaves nothing behind.
Look at your flakiest test first. Nine times out of ten the problem isn't the test itself, it's the data underneath. Build that data through a factory, isolate it from other tests, and measure the difference in stability. Do that a few times and you'll have the argument to tackle it structurally for the whole suite.
We'll review your test approach and tell you honestly where the gains are. And where they aren't.
Book a call