level 06 / sql-fundamentals

SQL Fundamentals for Testers

Write SQL queries to validate database state in automated tests — SELECT, JOIN, and WHERE.

Why Testers Need SQL

UI and API tests verify presentation and contract. SQL queries verify ground truth — what’s actually stored. Critical for:

  • Confirming a form submission persisted correctly
  • Verifying a delete didn’t orphan child records
  • Asserting a background job ran and updated rows
  • Checking data migrations didn’t corrupt records

SELECT Basics

-- All columns
SELECT * FROM customers;

-- Specific columns
SELECT id, email, created_at FROM customers;

-- Filter
SELECT * FROM orders WHERE status = 'pending';

-- Multiple conditions
SELECT * FROM orders
WHERE status = 'pending'
  AND created_at > NOW() - INTERVAL '24 hours';

-- Limit
SELECT * FROM products ORDER BY created_at DESC LIMIT 10;

WHERE Operators

-- Comparison
WHERE price > 100
WHERE quantity BETWEEN 1 AND 50
WHERE status IN ('active', 'pending')
WHERE email LIKE '%@example.com'
WHERE deleted_at IS NULL

-- Negation
WHERE status NOT IN ('deleted', 'archived')
WHERE name IS NOT NULL

JOINs

-- INNER JOIN — only matching rows
SELECT o.id, u.email, o.total
FROM orders o
INNER JOIN users u ON o.user_id = u.id;

-- LEFT JOIN — all orders, even without a user
SELECT o.id, u.email
FROM orders o
LEFT JOIN users u ON o.user_id = u.id;

-- Multiple joins
SELECT o.id, u.email, p.name AS product
FROM orders o
INNER JOIN users u ON o.user_id = u.id
INNER JOIN order_items oi ON oi.order_id = o.id
INNER JOIN products p ON oi.product_id = p.id;

Aggregate Functions

-- Count
SELECT COUNT(*) FROM orders WHERE status = 'completed';

-- Sum, average
SELECT SUM(total), AVG(total), MAX(total), MIN(total)
FROM orders
WHERE user_id = 42;

-- GROUP BY
SELECT status, COUNT(*) AS count
FROM orders
GROUP BY status;

-- HAVING (filter after aggregation)
SELECT user_id, COUNT(*) AS order_count
FROM orders
GROUP BY user_id
HAVING COUNT(*) > 5;

Using SQL in Playwright Tests

With pg (PostgreSQL client):

import { Client } from 'pg';
import { test, expect } from '@playwright/test';

let db: Client;

test.beforeAll(async () => {
  db = new Client({ connectionString: process.env.DATABASE_URL });
  await db.connect();
});

test.afterAll(async () => {
  await db.end();
});

test('registration creates user record', async ({ page }) => {
  await page.goto('/register');
  await page.fill('[name=email]', 'newuser@test.com');
  await page.fill('[name=password]', 'SecurePass123!');
  await page.click('[type=submit]');
  await expect(page.getByText('Welcome')).toBeVisible();

  const res = await db.query(
    `SELECT id, email, created_at FROM users WHERE email = $1`,
    ['newuser@test.com']
  );
  expect(res.rows).toHaveLength(1);
  expect(res.rows[0].email).toBe('newuser@test.com');
});

Common Tester Queries

-- Check record exists
SELECT COUNT(*) FROM users WHERE email = 'test@example.com';
-- Assert: count = 1

-- Check record was deleted
SELECT COUNT(*) FROM orders WHERE id = 123;
-- Assert: count = 0

-- Verify FK relationship intact
SELECT o.id FROM orders o
LEFT JOIN users u ON o.user_id = u.id
WHERE u.id IS NULL;
-- Assert: 0 rows (no orphaned orders)

-- Check latest record
SELECT * FROM audit_log
WHERE entity_id = 42
ORDER BY created_at DESC
LIMIT 1;