level 00 / test-design

Test Design Techniques

BVA, equivalence partitioning, and decision tables — the systematic techniques that turn requirements into a minimal, complete set of test cases.

Why technique matters

Untrained testers test what they think will pass. Trained testers use systematic techniques to find the cases that will fail. Test design is the difference between a test suite that proves you tested a lot and one that proves your software works correctly.

Boundary Value Analysis (BVA)

Most bugs live at boundaries. If a system accepts ages 18–65, bugs typically appear at 17, 18, 65, 66 — not at 35.

BVA rule: test at the boundary, one below, and one above.

InputValueExpected
Min boundary18Accept
Below min17Reject
Above min19Accept
Max boundary65Accept
Above max66Reject
Below max64Accept
// BVA test cases for an age validation function
test.each([
  [17, false],  // below min
  [18, true],   // min boundary
  [19, true],   // above min
  [64, true],   // below max
  [65, true],   // max boundary
  [66, false],  // above max
])('age %i → isValid: %s', async (age, expected) => {
  await page.locator('[data-testid="age-input"]').fill(String(age));
  await page.locator('[data-testid="submit"]').click();
  const valid = await page.locator('[data-testid="error"]').isHidden();
  expect(valid).toBe(expected);
});

Equivalence Partitioning (EP)

Group inputs into partitions where every value in the partition behaves the same. Test one representative value from each partition instead of testing every possible input.

Example — password validation: minimum 8 chars, must contain a digit.

PartitionExampleExpected
ValidSecret1!Accept
Too shortSec1!Reject — too short
No digitSecretABC!Reject — no digit
EmptyReject — required

Testing Secret1! covers the entire valid partition — no need to test Password2! or Mypass9@ separately.

Key insight: BVA and EP are complementary. EP divides the input space into partitions; BVA tests the edges of each partition. Use both together for thorough input coverage.

Decision Tables

When multiple conditions combine to determine an outcome, a decision table ensures you test every combination systematically.

Example — discount eligibility: a user gets a discount if they have a loyalty card AND have spent over £100, OR if they are a new user.

Loyalty cardSpend > £100New userDiscount?
Yes
No
No
Yes
Yes

A decision table forces you to enumerate all combinations. It commonly reveals business logic bugs that neither manual exploration nor individual condition testing would find.

// Decision table as Playwright test.each
test.each([
  [true,  true,  false, true],   // loyalty + spend = discount
  [true,  false, false, false],  // loyalty, no spend, not new = no discount
  [false, true,  false, false],  // no loyalty, spend = no discount
  [false, false, true,  true],   // new user = discount
])('loyalty:%s spend>100:%s newUser:%s → discount:%s', async (loyalty, spend, newUser, expected) => {
  // set up user state and navigate to checkout
  // assert discount badge visible/hidden
  expect(expected).toBeDefined(); // replace with real assertion
});

Applying the techniques to Playwright

ScenarioTechnique
Login form: valid/invalid username lengthsBVA
Credit card number field: empty/valid/too long/non-numericEP
Checkout flow: logged-in × cart-not-empty × payment methodDecision table
Age gate: 17/18/19BVA
Search results: 0 / 1 / many resultsEP + BVA

Interview questions