- Database testing
- SQL
- Manual testing
Testing Below the Interface: Why I Verify Results in SQL
The screen said the order was cancelled. The database said otherwise. A practical case for every manual tester learning enough SQL to check for themselves.
Early in my QA career I closed a ticket because the interface told me the operation had worked. The success toast appeared, the list refreshed, the row was gone. A week later the same record turned up in a report, because the delete had only ever been a soft flag that half the system ignored.
The interface is a rendering of the truth. It is not the truth. Once you can query the database yourself, you stop taking its word for it.
What the interface can hide
Four failure modes I now check for as a matter of routine:
Optimistic UI. The screen updates before the server confirms, so a failed write can look like a successful one until the next reload.
Partial writes. An operation touches three tables and the third fails. The screen reports success because the first two worked, and now the data is inconsistent in a way no user action can repair.
Display-time calculation. A total that is computed for display but stored incorrectly. The screen is right, the stored value is wrong, and the report that reads the stored value is wrong too.
Silent truncation. A field with a 50-character column and no client-side limit. The save succeeds, the value is cut short, and nobody notices until someone's address is missing its last line.
None of these are visible from the front end. All of them are obvious in a query.
The three queries that cover most of it
You do not need to be a database engineer. Three patterns handle the bulk of verification work.
Look at the record you just changed. Not the list view — the actual row, every column:
SELECT * FROM bookings WHERE id = 8412;
Then read it against what you expected. Status, timestamps, the user id, the soft-delete flag. This one query catches soft-delete surprises, wrong status transitions, and missing audit fields.
Check the related records changed too. After an operation that should cascade:
SELECT b.id, b.status, b.total,
p.status AS payment_status,
p.amount AS payment_amount
FROM bookings b
LEFT JOIN payments p ON p.booking_id = b.id
WHERE b.id = 8412;
A cancelled booking with a payment still marked as captured is a real defect, and it is completely invisible on the booking screen.
Hunt for records that should not exist. This is the one that finds bugs nobody reported:
-- Cancelled bookings that still hold an active payment
SELECT b.id, b.status, p.status
FROM bookings b
JOIN payments p ON p.booking_id = b.id
WHERE b.status = 'cancelled'
AND p.status = 'captured';
-- Orders whose stored total disagrees with their line items SELECT o.id, o.total, SUM(i.quantity i.unit_price) AS calculated FROM orders o JOIN order_items i ON i.order_id = o.id GROUP BY o.id, o.total HAVING o.total <> SUM(i.quantity i.unit_price); ```
That last query is worth writing for any system that stores a total. Run it against a staging database with real-shaped data and it will either return nothing — genuinely reassuring — or hand you a list of broken records and, with them, the reproduction case.
Rules I follow
Read-only, always. SELECT only. If a test needs data changed, change it through the application, because that is what you are testing. A tester who runs an UPDATE on a shared environment loses everyone's trust once.
Never touch production. Read access to a production database is a serious responsibility and usually unnecessary for testing. Staging with realistic data is where this work belongs.
Save your queries. Keep them next to the test cases they verify. They become part of the regression suite and the next tester inherits years of accumulated knowledge instead of starting over.
Learn the schema before you need it. Half an hour with the table list and the foreign keys tells you more about how the product actually works than a week of clicking through it.
Why this changes how you are seen
There is a practical career effect. A tester who reports "cancelling a booking doesn't work" starts a debate. A tester who reports "cancelling sets bookings.status to cancelled but leaves the related payments row as captured — here is the query and the affected ids" has done half the developer's investigation for them.
The second report gets fixed the same day. It is also the report that gets you into the conversations where technical decisions are made — and that, more than any certificate, is what moves a manual tester towards a lead role.