How to Reset Your Database During E2E and API Testing
Dirty databases cause flaky E2E and API tests. Learn why resetting test data matters, compare common cleanup approaches, and restore a known dataset with Seedmancer before CI and Playwright runs.
End-to-end and API tests are most valuable when they are reliable. A good test should pass when the application works and fail when something is actually broken.
But in many projects, tests fail for a different reason: the database is dirty.
A previous test created a user. Another test changed an order status. A deleted record is still missing. The next test starts with unexpected data, and suddenly your test suite becomes flaky.
This is why resetting the database during E2E and API testing is so important.
In this article, we will look at why database resets matter, common ways to reset test data, and how Seedmancer can help you reset your database quickly and consistently.
The problem: tests depend on database state
E2E and API tests usually interact with a real application and a real database. That is useful because the tests are close to actual user behavior.
However, it also means tests can affect each other.
For example:
- Test A creates a new user with
test@example.com. - Test B also tries to create a user with
test@example.com. - Test B fails because the email already exists.
The application may be working correctly, but the test fails because the database was not cleaned up.
This kind of failure is frustrating because it wastes time. Developers start asking:
- Did the feature break?
- Did the test break?
- Did another test leave bad data behind?
- Does this only happen in CI?
When test results are not trustworthy, the whole team loses confidence in the test suite.
Why resetting the database helps
Resetting the database means returning it to a known state before or after a test runs.
This gives every test a clean and predictable starting point.
1. Tests become more reliable
A clean database helps remove hidden dependencies between tests.
Each test can run with the data it expects, instead of depending on what happened before.
This reduces flaky failures and makes test results easier to trust.
2. Tests become easier to debug
When a test fails, you want to know that the failure is related to the behavior being tested.
If the database state is unknown, debugging becomes harder. You may need to inspect old records, manually clean tables, or rerun tests in a different order.
With database resets, the test environment is predictable. That makes failures easier to reproduce and fix.
3. Tests can run independently
Good tests should not depend on execution order.
For example, a checkout test should not depend on a user created by a previous login test. It should create or seed the data it needs.
Resetting the database makes this easier because each test starts from the same baseline.
4. CI becomes more stable
Database state problems often become worse in CI.
CI environments may run tests in parallel, reuse containers, or fail halfway through a previous test run. If the database is not reset correctly, the next run can start from a broken state.
A reliable reset process makes CI test results more stable.
5. Developers can test locally faster
When tests leave messy data behind, developers often need to manually reset the local database.
That may involve running SQL commands, recreating containers, or reapplying migrations.
A fast reset command improves the local development workflow. Developers can run tests repeatedly without thinking about database cleanup.
Common ways to reset a test database
There are several ways to reset a database during testing. Each approach has trade-offs.
Option 1: delete data after each test
One common approach is to delete test data after each test finishes.
For example:
DELETE FROM orders;
DELETE FROM users;
This can work for small projects, but it becomes harder as the database grows.
You need to delete tables in the correct order because of foreign key constraints. You also need to remember every table that might be affected by a test.
If one cleanup step is missed, the next test may fail.
Option 2: truncate all tables
Another approach is to truncate all tables before each test or test suite.
TRUNCATE TABLE users, orders, products RESTART IDENTITY CASCADE;
This is usually faster than deleting rows one by one. It also resets auto-incrementing IDs.
However, truncation must be handled carefully. Some tables may need to keep reference data, such as countries, roles, permissions, or product categories.
Option 3: drop and recreate the database
You can also drop the database and recreate it from scratch.
This gives you a very clean environment, but it can be slow.
For large applications, running all migrations and seed scripts before every test can take too much time.
Option 4: use database snapshots
A more advanced approach is to create a snapshot of a clean database and restore it when needed.
This can be fast and reliable, especially when the database has complex seed data.
The challenge is that snapshot management can become complicated if you build it yourself.
When should you reset the database?
There are three common timings.
Before each test
This gives the strongest isolation.
Every test starts from a clean state, so tests are less likely to affect each other.
The downside is speed. Resetting before every test can be expensive if the reset process is slow.
Before each test file or test suite
This is a practical balance for many teams.
You reset the database before a group of related tests, then let tests inside that group share setup when appropriate.
This can be faster, but you need to be careful not to create hidden dependencies between tests.
Before a CI run
At minimum, the database should be reset before a CI test run starts.
This prevents old data from previous runs from affecting the current result.
However, this alone may not be enough if tests inside the same run affect each other.
What is Seedmancer?
Seedmancer is a CLI-based tool for resetting and managing test database state.
It is designed for E2E and API testing workflows where developers need a fast, repeatable way to prepare the database before tests run.
The goal is simple:
Make database resets predictable, fast, and easy to use in local development and CI.
Seedmancer is especially useful when you want to avoid writing fragile cleanup scripts manually.
Instead of guessing which rows to delete, you restore a named dataset — a CSV-backed snapshot aligned to your schema — so Postgres is brought back to a known-good state in one command (seed). Pick a stable id once (often from seedmancer export, generate-local, or a checked-in baseline) and reuse it everywhere.
How Seedmancer helps
1. One command restores a known baseline
Instead of manually deleting rows or recreating the database from scratch each time, you restore a labeled dataset:
seedmancer seed --id baseline --yes
-
Replace
baselinewith your team’s dataset id (same name you used at export or generate time — for example what you ship in.seedmancer/for CI). -
--yesskips the confirmation prompt so shells and pipelines do not hang.
That single step truncates targeted tables per your schema fingerprint and reloads CSVs — effectively resetting the DB to your chosen scenario before tests run locally, from npm scripts, or in CI.
2. Consistent test data
Many tests need some initial data before they run.
For example:
- An admin user
- Default roles and permissions
- Product categories
- Test accounts
- Organization settings
Once that state lives in your dataset, every test starts from the same predictable rows without re-running lengthy setup migrations for each case.
3. Better local developer experience
When working locally, developers often run the same E2E or API test many times.
Without a reset workflow, the database becomes messy quickly.
With Seedmancer, developers can restore the baseline whenever they need:
seedmancer seed --id baseline --yes
This makes it easier to reproduce bugs and rerun tests with confidence.
4. CI-friendly workflow
Seedmancer can also be used as part of a CI pipeline.
For example, before running API tests:
seedmancer seed --id baseline --yes
npm run test:api
Or before running E2E tests:
seedmancer seed --id baseline --yes
npx playwright test
This helps ensure CI starts from a clean database state every run.
Example: API tests without duplicate rows
Imagine you have an API test that creates a user.
Without resetting the database, the test might fail if the same email already exists.
import { test, expect } from "@playwright/test";
test("creates a new user", async ({ request }) => {
const response = await request.post("/api/users", {
data: {
email: "test@example.com",
name: "Test User",
},
});
expect(response.status()).toBe(201);
});
If this test runs twice against the same database, the second run may fail.
Restore your baseline dataset before the test job:
seedmancer seed --id baseline --yes
npx playwright test
Now the test can start from a clean state every time.
Example: Seedmancer with Playwright in npm scripts
You can compose the seed step into a package script so every developer shares the same flow:
{
"scripts": {
"test:e2e": "seedmancer seed --id baseline --yes && playwright test"
}
}
Then run:
npm run test:e2e
Swap baseline for the dataset id your project documents (matching what you restore in CI).
Best practices for database resets during testing
Keep test data small
Large seed data can make tests slow.
Only create the data your tests actually need — or partition datasets by scenario (smoke, billing, …) instead of loading everything everywhere.
Avoid depending on test order
Each test should either create the data it needs or depend on a clearly defined baseline.
Do not rely on data created by another test unless that dependency is intentional and obvious.
Reset IDs when needed
Some tests expect predictable IDs.
For example, a test may expect the first created user to have ID 1.
Seedmancer’s restore reloads CSVs consistently; pairing that with truncation and sequence behavior from your dialect helps keep identifiers predictable compared to drifting manual inserts across runs.
Separate reference data from test data
Some data should always exist, such as roles or permissions.
Other data should be created only for specific tests.
Keeping this distinction clear makes reset logic safer.
Use the same workflow locally and in CI
The closer your local test environment is to CI, the fewer surprises you will have.
Use the same seedmancer seed command and dataset id in both places when possible.
See the CLI documentation for flags like --env, Connectors (auth, Stripe), and how datasets map to schema fingerprints.
Conclusion
Resetting the database is one of the most effective ways to make E2E and API tests more reliable.
It gives every test a predictable starting point, reduces flaky failures, makes debugging easier, and improves CI stability.
You can reset a database in many ways, such as deleting rows, truncating tables, recreating the database, or restoring snapshots. Maintaining bespoke cleanup scripts tends to hurt as schemas and FK graphs grow — a single forgotten table can flake the whole suite.
Seedmancer gives teams one repeatable lever: restore a labeled dataset aligned to their schema fingerprint so Postgres returns to the state their tests assume.
If your E2E or API tests are flaky because of database state, adding a restore-before-tests workflow is a high-leverage fix.
Often it boils down to:
seedmancer seed --id baseline --yes
npm run test:e2e
Clean database. Reliable tests. Faster development.