---
title: "Test Design Techniques: Pick the Few Tests That Find Bugs"
author: "Harry"
published: 2026-10-31T04:45:50.709Z
updated: 2026-10-31T05:06:15.002Z
category: "QA & Testing"
tags: ["qa-skills-path"]
canonical: "https://fxblog.dev/posts/test-design-techniques"
---

# Test Design Techniques: Pick the Few Tests That Find Bugs

## Key takeaways

- Equivalence partitioning picks one value per group the system treats the same way; boundary value analysis then tests the edges of each group.
- For a rule like a €50.00 minimum, the three values €49.99, €50.00 and €50.01 test the edge where a developer writes \< instead of \<=.
- Test invalid values one at a time, or you can't tell which check rejected the input.
- A decision table's unanswered cells are questions for the product owner, and are often its most useful output.
- An exploratory session has a charter, a 60 to 90 minute time box, notes taken during the session and a debrief.
- A bug report that gets fixed names where, what and when in the title, gives exact numbers, and says what was already ruled out.

> **TL;DR** Testing with whatever values come to mind costs you the bug at the edge and a coverage claim nobody can check. Test design techniques pick the few values most likely to fail: partitions, boundaries and a decision table for rules that combine. A timed exploratory session finds what no rule mentions, and a precise bug report gets it fixed.
>
> **Do this:** Take one rule with a number in it from your own product and write its three boundary tests: just below, on, and just above the limit.

Part 1 left the ShopLite checkout spec untested: six rules and millions of possible inputs. Which values for `SAVE10` are worth a test, and what happens to the rule the spec forgot?

Start with a mistake that looks like diligence. Five valid quantities, 1, 3, 5, 7 and 9, find fewer bugs than four values at the edges: 0, 1, 10 and 11.

**The QA Skills Path, in seven parts:**

1. **The QA Skills Path for 2026: What to Learn, in What Order**
2. **Test Design Techniques: Pick the Few Tests That Find Bugs** (you are here)
3. **Test Below the UI: Your First API Tests in Python**
4. **Why Your UI Tests Flake, and the Fix for Web and Mobile**
5. **Green Tests, Broken Checkout: The Checks Functional Tests Miss**
6. **Quality Gates in CI: Make Your Tests Block a Bad Merge**
7. **AI in Testing: Use It Without Trusting It, and Test AI Features**

## Pick the groups, then the edges

Equivalence partitioning splits an input into groups the system should treat the same way; one value per group is enough. Boundary value analysis then tests the edges of each group, because that is where a developer writes `<` instead of `<=`.

Rule R3 says `SAVE10` needs a subtotal of at least €50.00. Money moves in steps of €0.01, so three values cover the edge:

| Subtotal | Expected                              |
| -------- | ------------------------------------- |
| €49.99   | Code rejected: "Minimum order €50.00" |
| €50.00   | 10% off → €45.00                      |
| €50.01   | 10% off → €45.01 (rounding 45.009 up) |

The same move works on every number in the spec: quantity 0, 1, 2 and 9, 10, 11; code lengths 3, 4, 5 and 11, 12, 13.

> **Tip**
>
> Test valid values together if you like, but invalid ones one at a time. Send quantity `-3` and code `AB` together, get an error, and you can't tell which check fired, or whether the other one exists.

## A table finds the rule nobody wrote

When an outcome depends on a combination, list every combination. Codes R3 and R4 interact, so:

|                | T1      | T2              | T3            | T4                |
| -------------- | ------- | --------------- | ------------- | ----------------- |
| Code           | SAVE10  | SAVE10          | FREESHIP      | SAVE10 + FREESHIP |
| Subtotal ≥ €50 | Y       | N               | –             | Y                 |
| Result         | 10% off | "Minimum order" | Free shipping | **?**             |

The spec says the codes can't be combined, but not which error T4 shows, or whether the first code stays. That question mark is the most useful cell: take it to the product owner before the code is built.

Two more techniques work the same way. State-transition testing checks the moves that must be refused, such as a payment webhook arriving after a cancel. Pairwise testing covers payment, code, device and customer type in about 9 or 10 tests instead of 36.

## Explore what no rule mentions

Designed tests only check what someone thought of. An exploratory session finds the rest, and it has a shape:

One session-based exploratory session

```mermaid
flowchart LR
  C[Charter: explore X with Y to discover Z] --> S[60 to 90 minutes]
  S --> N[Timestamped notes]
  N --> D[10 to 15 minute debrief]
  D -->|next charter| C
  N -->|bug| R[Bug report]
```

```text
14:03  Cart: 2 × Mug (€30.00) = €60.00. Applied SAVE10 → €54.00 ✓
14:07  Reduced to 1 Mug (€30.00). Total shows €27.00. Discount still applied below €50 ❓
14:09  Refreshed page → still €27.00. Paid with test card → order #A-1042 total €27.00 🐞
14:15  Same steps via API (PATCH /cart/lines) → API removes code ✓. UI-only problem?
```

No row in the boundary table changes the cart after the code is in. The charter did, and found a bug in four minutes.

> **Warning**
>
> Write notes during the session, not after. Forty minutes later the exact steps and amounts are gone, and so is the bug report.

## Write the bug so it gets fixed

A bug nobody understands stays open. The title says where, what and when:

- Bad: `Discount broken`
- Good: `Checkout: SAVE10 stays applied after cart drops below €50 minimum`

Then: environment, preconditions, only the steps needed, the expected result with its source (rule R3), the actual result in exact numbers, frequency and evidence. The repo's BUG-311 adds one more line, what was already ruled out: the API recalculates correctly when called directly, so the fault looks like stale state in the page. Severity is your proposal; priority is the product owner's call.

## Try it

```text
Explore <one feature> with <inputs, tools or tricks>
to discover <the kind of problem you're after>.
Time box: 60 minutes. Notes with timestamps. Debrief: 10 minutes.
```

You now have a short list of ShopLite tests you can defend, and BUG-311 written up. But look at the 14:15 line again: the API removed the code, and only the page got it wrong. You checked that once, by hand. How do you test the API itself, every time, without a browser? Part 3, **Test Below the UI: Your First API Tests in Python**, writes those tests in code.

## Read this with your AI

```text
Read the post "Test Design Techniques: Pick the Few Tests That Find Bugs".
1. I'll paste one rule from my product. List its partitions and 3-value boundaries.
2. Build a decision table for any rules that combine, and mark the cells the rule doesn't answer.
3. Write one exploratory charter for it, in the explore-with-to-discover form.
```

> **Success**
>
> You can pick the few tests that matter, explore for the rest, and report what you find. Next: **Test Below the UI: Your First API Tests in Python**.

## FAQ

### What is the difference between equivalence partitioning and boundary value analysis?

Equivalence partitioning splits inputs into groups that should behave the same and tests one value from each. Boundary value analysis tests the values at and around the edges of those groups, where off-by-one mistakes happen.

### Is exploratory testing just clicking around?

No. A session has a written charter that says what to explore, with what, to discover what; a fixed time box; notes taken as you go; and a debrief afterwards. That makes it reviewable.

### Who sets the priority of a bug?

The tester proposes the severity, which is how bad the effect is. The product owner or team decides the priority, which is how soon it must be fixed.
