tldr: Database testing verifies schema, data integrity, ACID behavior, performance under load, and migration correctness. Skipped or done casually, it produces the bugs that take production down at 3am.
What database testing covers
Database testing splits into four concerns:
- Schema. Tables, columns, types, indexes, constraints match the design.
- Data integrity. Foreign keys hold, nulls only where allowed, no duplicate keys.
- Logic. Stored procedures, triggers, views, and functions return correct results.
- Performance. Queries return within acceptable time at expected scale.
Application-level tests catch some of this indirectly. A dedicated database test layer catches all of it directly.
Why skipping it is risky
Most production incidents that take a service offline trace back to one of three database problems: a missing index, a bad migration, or a constraint violation that surfaced under load.
None of those are easy to find with end-to-end tests. They show up only when the database is hit hard, with realistic data, in conditions that match production.
Schema and migration tests
Every schema change should ship with tests that verify:
- The migration applies cleanly on a fresh database.
- The migration applies cleanly on a database with existing data.
- The migration is reversible (or, if not, that is a documented decision).
- Constraints (NOT NULL, FOREIGN KEY, UNIQUE) behave correctly after the migration.
Tools like Flyway, Liquibase, and Alembic offer migration test harnesses. CI runs them against a fresh container before any code merges.
Data integrity tests
Run periodically, ideally on a snapshot of production data:
- No orphan rows (foreign key targets exist).
- No invalid enums (status values are within the allowed set).
- No duplicate unique keys (sometimes inserted before constraints existed).
- No corrupted timestamps (dates in 1970 or year 9999).
These checks find data debt that accumulated before constraints were added. They will not find every problem, but they catch the cliffs.
Stored procedure and trigger tests
If you have business logic in the database, treat it like any other code. Test it directly.
- Use pgTAP for Postgres, tSQLt for SQL Server.
- Test with known inputs and known expected outputs.
- Cover error cases (invalid inputs, missing rows).
- Run the tests in CI on every change.
Stored procedure logic that is not tested directly always rots. The team forgets it exists, the test suite misses it, and one day a query starts returning wrong totals.
Performance tests
Three things to measure consistently:
- Query plans. Run EXPLAIN ANALYZE on every critical query in CI. Fail the build if a query starts using a sequential scan when it should use an index.
- P95 latency. Most teams care about average latency. P95 is what users actually feel. Track it per critical query.
- Connection pool saturation. A query that performs fine in isolation may exhaust the pool under load.
Pair this with realistic data volumes. A query that returns in 50ms on a 1,000-row table can return in 5 seconds on a 10-million-row table.
Backend testing vs database testing
Backend testing covers application logic, APIs, and integrations. Database testing zooms into the database layer specifically. The two overlap but are not the same.
A backend test might verify "the order endpoint returns 200 with a valid order ID." A database test verifies "the order_items table got the correct row, the inventory was decremented atomically, and the index was used for the lookup."
Tools that get used
- pgTAP, tSQLt, utPLSQL for in-database test frameworks.
- DBUnit, Testcontainers for application-level integration tests against real databases.
- Liquibase, Flyway for migration management with built-in testing.
- pgbench, sysbench for synthetic load.
Application-level test frameworks like Pytest and JUnit also work fine for database integration tests with the right fixtures.
How AI testing fits
End-to-end tests with AI testing platforms like Bug0 catch database bugs indirectly: the order page shows wrong totals, the dashboard displays stale data, the user list contains a duplicate. The platform reports the symptom, not the cause.
You still need direct database tests for the underlying schema and logic. They are complementary.
FAQs
Should developers or QA write database tests?
Whoever owns the schema and queries. In most teams, that is engineering. QA focuses on behavior visible from the application.
How often should performance tests run?
In CI for query plan checks. Nightly or weekly for full load tests. Pre-release for capacity validation.
What about NoSQL databases?
Same principles, different mechanics. Schema tests become document-shape validation. Performance tests focus on partition keys and read/write patterns. Tools like Mongoose schema validators or DynamoDB local provide test harnesses.
How do you handle test data privacy?
Mask or synthesize. Production data in test databases is a compliance failure. Use tools like Tonic, Mockaroo, or your own scripts.
Can Bug0 catch database bugs?
Bug0 catches the user-facing effects of database bugs (wrong totals, missing data, slow pages). Direct database validation still belongs in your CI pipeline.
