Test Cases·Field Guide
DARK
SE401 · Software Quality Assurance and Testing · Chapter 08

Test cases:
the recipe card for finding bugs.

A test case isn't a magic wand that makes software better — weighing yourself doesn't reduce your weight, and going to the doctor doesn't make you healthy. What it does is give you a precise, repeatable recipe: exact inputs, exact steps, exact expected outcome — so that anyone, any time, can run the same check and get the same answer. This chapter is about writing that recipe card well.

5-part anatomy
Triangle worked example
Scaffolding & oracles
Exam-tested
TC-01 · TEST CASE NAME REQUIREMENT PRECONDITIONS STEPS EXPECTED RESULT
§ 01 — WHAT TESTING ACTUALLY DOES

Testing doesn't fix your software

Get this uncomfortable truth locked in before anything else — it reframes everything that follows.

⚖️
Memory hook
"The scale doesn't diet for you."
Weighing yourself doesn't reduce your weight. Going to the doctor doesn't make you healthy. Testing is the same — it doesn't improve the software. That's the developer's job. Testing just tells you where to look.

The massive misunderstanding

from the slides, verbatim idea

There is a massive misunderstanding about testing: that it improves software. It doesn't. What testing (and the scale, and the doctor's visit) actually does is help identify problems that you might then choose to resolve. Testing does not make the product better — even though it's part of a larger process that does make a product better.

Takeaway: testing is the diagnostic step, not the cure. The cure is whatever the developer does after reading the test results.
§ 02 — ANATOMY OF A TEST CASE

Five parts, one repeatable recipe

Three overlapping definitions from the deck, all pointing at the same thing.

Definition 1: a set of conditions under which a tester will determine whether an application, software system, or one of its features is working as it was originally established to do.
Definition 2: a description of a specific interaction that a tester will have in order to test a single behavior of the software.
Definition 3: a set of actions executed to verify a particular feature or functionality of your software application. Test cases are often called test scripts — especially once written down and collected into test suites. They're very similar to use cases: both are step-by-step narratives defining a specific interaction between user and software.
🧩
Memory hook
"Name, Reason, Ready, Do, Done."
The five slots of a typical test case table, in order: unique Name/number → the Requirement it exercises (the reason) → Preconditions (ready state) → Steps (what you do) → Expected Results (what "done" looks like).
1 · Name / Number

A unique identifier

So a specific test case can be referenced, tracked, and re-run — e.g. TC-47.

2 · Requirement

The requirement this test case is exercising

Ties the test back to a documented requirement — e.g. FR-4, "case sensitivity in search-and-replace."

3 · Preconditions

The state of the software before the test

What must already be true/loaded/set up before step 1 can run — e.g. "test document TESTDOC.DOC is loaded (base state BS-12)."

4 · Steps

The specific steps that make up the interaction

A numbered, exact sequence of actions — click this, type that — with concrete data, not vague descriptions.

5 · Expected Results

The expected state of the software after execution

Every test case must have an expected result. No expected result means there's nothing to compare against — and nothing to call a pass or a fail.

Non-negotiable property: test cases must be repeatable. Good test cases are data-specific — they describe each interaction necessary to repeat the test exactly, not "enter a name" but "enter This is the Search Term."
§ 03 — THE WRITING PROCESS

Before you write a single step

Prep work
🔤

1. Understand the language fundamentals

Sizes and limits of variables, platform-specific information.

🗺️

2. Understand the domain

What real-world problem is this software solving?

📄

3. Read the requirements

The spec is the ground truth for what "correct" means.

Think from three angles
🙋

Think like a user

What possible things do they want to do?

Think about possible "mistakes"

i.e. invalid input — what will a careless or confused user type?

🚫

Think about impossible conditions

Inputs or states that should never legitimately occur — test them anyway.

What is the testing intended to prove?

3 goals
Correct operation — gives correct behavior for correct input.
Robustness — responds to incorrect or invalid input with proper results (doesn't crash, doesn't silently corrupt data).
User acceptance — handles typical, realistic user behavior gracefully.
Then, finally: write down the test cases. Thinking about them isn't enough — an unwritten test case can't be repeated, assigned, or tracked.
§ 04 — WRITING GOOD TEST CASES

Seven habits of a strong test case

🧠
Memory hook
"Simple Users Repeat, Don't Assume — Cover, Identify, Technique."
Simple & transparent · User in mind · avoid Repetition · Don't assume (stick to spec) · Cover 100% · Identifiable · use Techniques (like BVA)
🔍

Simple & transparent

Anyone should be able to read it and immediately know what it's checking.

🙋‍♀️

End user in mind

Write it the way a real user would interact with the system, not just how a developer would.

♻️

Avoid repetition

Don't write ten test cases that all test the same thing in slightly different words.

📌

Do not assume

Stick to the specification documents — don't fill gaps with guesses about intended behavior.

💯

Ensure 100% coverage

Every requirement should be traceable to at least one test case.

🏷️

Must be identifiable

Unique names/numbers so a failing test can be reported and tracked precisely.

Implement testing techniques

you can't test everything

It's not possible to check every possible condition in your software application. Testing techniques help you select a few test cases with the maximum possibility of finding a defect.

Boundary Value Analysis (BVA)
BVA: testing of boundaries for a specified range of values — the edges are where off-by-one bugs live.
Repeatable and self-standing: the test case should generate the same results every time, no matter who runs it.
What a draft must include
The description of what requirement is being tested.

Inputs and outputs, or actions and expected results — a test case must have an expected result.
Verify the results are correct against 4 lenses
✓ Testing normal conditions
✓ Testing unexpected conditions
✓ Bad (illegal) input values
✓ Boundary conditions

Three angles of coverage, in general

applies to any input

Cover all valid input

Try multiple sets of values, not just one. Try permutations of values.

📏

Check boundary conditions

Check for off-by-one conditions specifically.

Check invalid input

Illegal sets of value, illegal input / impossible conditions, and totally bad input (text vs. numbers, etc.).

§ 05 — WORKED EXAMPLE

The triangle-classification problem

The deck's running example for applying the three coverage angles above to a real function: classify a triangle given three side lengths.

Valid input

cover all 3 shapes

All three possible conditions: equilateral, isosceles, scalene. Try multiple sets of values, and permutations of the same set.

Permutations to try: {3,4,5}, {4,3,5}, {5,4,3} — same triangle, different argument order.

Boundary conditions

off-by-one

Check the off-by-one edges of whatever range the values can take.

Values to try: 0 and MAX_INT — the smallest and largest representable side lengths.

Invalid input

three distinct flavors
Illegal sets of value
Wrong format: not integer. Negative numbers.
Illegal input — impossible conditions
{2,3,8}, {2,3,5} — these fail the triangle inequality (the definition of a triangle), so no actual triangle can have these sides.
Totally bad input — text vs. numbers, etc. (e.g. passing the letter "x" where a side length is expected).

How to actually run it

stdin / stdout

Have the code read from standard input and print to standard output, so the whole test suite can run unattended and get diffed against expected output.

java Triangle < testcases.txt
 
# or interactively:
java Triangle
2 3 3
3 4 5
 
# print output to a file:
java Triangle > results.txt
 
# combine both:
java Triangle <testcases.txt >results.txt
This assumes the code reads three numbers per line, or reads three numbers regardless of whether they're on one line or split across several — a detail worth pinning down before you write the test file.
§ 06 — COMPARISON PITFALLS

Beware of problems with comparisons

Two comparison bugs that are easy to write and easy to test for once you know to look.

Comparing floats

never do this
float a, b;
...
if (a == b)   // NEVER

Is it 4.0000000, or 3.9999999, or 4.0000001? What is your limit of accuracy? Exact float equality is almost never the right check.

Objects vs. references

OO languages
String a = "Hello world!\n";
String b = "Hello world!\n";
if ( a == b )        // reference check
vs.
if ( a.equals(b) )   // content check

In object-oriented languages, make sure you know whether you're comparing the contents of an object or the reference to an object — they can silently give different answers.

§ 07 — GOOD VS. BAD, SIDE BY SIDE

The same feature, tested two ways

Both examples are testing search-and-replace case sensitivity. Only one of them could actually be repeated by a stranger.

TC-47 — the good example

✓ data-specific
NameTC-47: Verify that lowercase data entry results in lowercase insert
RequirementFR-4 (Case sensitivity in search-and-replace), bullet 2
PreconditionsThe test document TESTDOC.DOC is loaded (base state BS-12).
Steps1. Click "Search and Replace." 2. Click in "Search Term" field. 3. Enter This is the Search Term. 4. Click in "Replacement Text" field. 5. Enter This IS THE Replacement TeRM. 6. Verify "Case Sensitivity" checkbox is unchecked. 7. Click OK.
Expected results1. Search-and-replace window is dismissed. 2. Verify that in line 38, this is the search term has been replaced by this is the replacement term. 3. Return to base state BS-12.

The bad example

✗ vague, not repeatable
Steps1. Bring up search-and-replace. 2. Enter a lowercase word from the document in the search term field. 3. Enter a mixed-case word in the replacement field. 4. Verify case sensitivity is not turned on and execute the search.
Expected results1. Verify that the lowercase word has been replaced with the mixed-case term in lowercase.
No name, no requirement traceability, no preconditions, and — critically — no concrete data. "A lowercase word" isn't a step two different testers can run identically.
§ 08 — TRUSTWORTHY TESTS

Hygiene rules for the test code itself

🎯
Memory hook
"One thing, one assert, no logic."
Three rules that keep a test trustworthy: test one behavior per test method, assert as little as possible per method, and keep control-flow logic out of the test itself.
🧵

One thing at a time per test method

10 small tests are much better than 1 test that's 10x as large.

☝️

Few (likely 1) assert statements

If you assert many things, the first failure stops the test — you won't know whether a later assertion would also have failed.

🧮

Avoid logic

Minimize if/else, loops, switch. Avoid try/catch — if it's supposed to throw, use expected=; if not, let the test framework catch it.

Torture tests are okay — but only in addition to simple tests, never as a replacement for them.
§ 09 — TEST EXECUTION

Running the plan, then running it again

When does it start?

the alpha build

Software testers begin executing the test plan after the developers deliver the alpha build — a build they feel is feature-complete. The alpha should already be high quality: developers should feel it's ready for release and as good as they can get it.

Iteration 1 — New functionality

Focus on what's new

First pass targets the newly added or changed features.

Iteration 2+ — Regression testing

Make sure nothing else broke

Verify a change to one area of the software hasn't caused problems anywhere else. Regression testing usually means re-executing all test cases previously executed. There are typically at least two regression tests for any software project.

Focus: did fixing X quietly break Y?

When is testing complete?

table 8-6

Either no defects are found, or defects meet the acceptance criteria outlined in the test plan. A representative acceptance-criteria checklist:

1Successful completion of all tasks documented in the test schedule.
2Quantity of medium- and low-level defects at an acceptable level, per the QA team lead.
3User interfaces for all features are functionally complete.
4Installation documentation and scripts are complete and tested.
5Development code reviews complete; all high-priority issues resolved.
6All outstanding issues pertinent to this release are resolved and closed.
7All code under source control, build process automated, components labeled with correct version numbers.
8All high-priority defects corrected and fully tested prior to release.
9Unfixed defects reviewed by stakeholders and confirmed acceptable.
10The end-user experience is at an agreed acceptable level.
11Operational procedures written for install, setup, error recovery, escalation.
12No adverse effects on already-deployed systems.
Automating test execution: designing test cases and test suites is creative — a demanding intellectual activity requiring human judgment, like any design activity. But executing them should be automatic: design once, execute many times. Automation separates the creative human process from the mechanical process of execution.
§ 10 — SCAFFOLDING

The code you build to support testing

From test case specifications to concrete test cases: test design often yields specifications ("a large positive number," "a sorted sequence, length > 2") rather than literal data. Generation turns those into concrete, executable test cases.

Scaffolding, defined

like construction scaffolding

Code produced to support development activities (especially testing) — not part of the "product" as seen by the end user, and may be temporary, just like scaffolding around a building under construction. Includes test harnesses, drivers, and stubs.

Real examples: JUnit is a test harness. Eclipse bundles an IDE, scaffolding, and JUnit built in.
🎬

Test driver

A "main" program for running a test. May be produced before the "real" main program, and gives more control — it exists to drive the program under test through its test cases.

🧱

Test stub

A substitute for called functions/methods/objects that the code under test depends on, but that aren't being tested right now.

🖥️

Test harness

Substitutes for other parts of the deployed environment — e.g. a software simulation of a hardware device.

Stubs — testing "Top" and "A" only Top A stub B stub C stub X stub Y

Real modules (solid) surrounded by stubs (dashed) that stand in for anything not currently under test.

Drivers — how general should scaffolding be?
Build a driver + stubs for each individual test case…
…or factor out common driver/management code (e.g. JUnit)…
…or factor further to drive a large number of test cases from data (e.g. DDSteps)…
…or go further still and generate the data automatically from a more abstract model (e.g. a network traffic model).
It's a question of cost vs. reuse — exactly like any other kind of software.
§ 11 — ORACLES

How do you know it passed or failed?

No use running 10,000 test cases automatically if the results still have to be checked by hand.

Comparison-based oracle

predicted output

Needs a predicted output for every input. The oracle compares actual output to predicted output and reports failure if they differ. Fine for a small number of hand-generated test cases — e.g. hand-written JUnit tests, where assert is the specific oracle coded by hand in each test case.

TEST CASE Test Input Expected Output COMPARE Program Pass/Fail

Self-checking code

judge without predicting

The oracle is written as self-checks inside the program itself — often possible to judge correctness without predicting the exact result. Usable with large, automatically generated test suites, but often only a partial check (e.g. structural invariants of data structures) — recognizes many or most failures, but not all.

Test Input Program Under Test Self-checks Failure Notif.
Range of specific to general applies here too — from a hand-coded assert per test case, up through a general comparison-based oracle with predicted output, up to self-checking code. None of these is "the only approach."

Capture and replay

for GUI testing

Sometimes there's no alternative to human input and observation — even after separating program functionality from the GUI, some GUI testing is still required. You can at least cut the repetition of human testing: capture a manually run test case, then replay it automatically.

With a comparison-based test oracle, "behavior same as previously accepted behavior." Reusable only until a program change invalidates it — lifetime depends on the abstraction level of the input and output.

Defect tracking

workflow

A defect tracking system records and tracks defects. It routes each defect between testers, developers, the project manager, and others, following a workflow designed to ensure the defect is verified and repaired.

Smoke tests

shallow but fast

A subset of test cases typically representative of the overall test plan. Good for verifying proper deployment or other non-invasive changes, and for confirming a build is ready to send to test. Not a substitute for actual functional testing.

§ 12 — CHEAT SHEET

The whole chapter in two tables

ConceptWhat it meansAnchor example
Test caseSet of conditions/steps a tester runs to check one behavior against expected resultsTC-47
5 partsName · Requirement · Preconditions · Steps · Expected Results TC-47 table
BVATesting the boundaries of a value range0, MAX_INT triangle sides
ScaffoldingSupport code: drivers, stubs, harness — not shipped to users JUnit, Eclipse
Test driver"Main" program that drives code under test through test cases Custom main / JUnit runner
Test stubStand-in for a called function/method/object not under test stub B, stub X
OracleDecides pass/fail — comparison-based (predicted output) or self-checking (invariants)assert vs. structural checks
Capture & replayRecord a manual GUI test once, replay automatically Comparison-based replay
Regression testingRe-run all previously-passed tests after a change ≥ 2 rounds per project
Smoke testRepresentative subset confirming a build is deployable Not a full functional pass
Invalid-input flavorMeansTriangle example
Illegal sets of valueWrong format or negative numbers"not integer", -3
Illegal inputImpossible conditions{2,3,8}, {2,3,5}
Totally bad inputWrong type entirelyText vs. numbers
§ 13 — COMMON MISTAKES

Where students actually lose points

Every one of these is a real mix-up the slide content sets up directly.

1. "Testing improves the software"

✗ WRONG

Running more tests makes the product better on its own.

✓ RIGHT

Testing only identifies problems. It's part of a process (design → fix → retest) that improves the product — the testing step itself doesn't. Like the scale not reducing your weight.

2. A test case without an expected result

✗ WRONG

Writing steps and calling it done — "click OK, see what happens."

✓ RIGHT

A test case must have an expected result. Without one there's nothing to compare the actual outcome to, and no way to call it pass/fail.

3. Vague, non-data-specific steps

✗ WRONG

"Enter a lowercase word" or "a mixed-case word" (the bad TC example).

✓ RIGHT

Good test cases are data-specific: "Enter This is the Search Term." Two different testers running vague steps can get different results — that breaks repeatability.

4. float a == b

✗ WRONG

Comparing two floating-point numbers with == and expecting a reliable true/false.

✓ RIGHT

Floats accumulate rounding error (4.0000000 vs 3.9999999) — compare within a tolerance / limit of accuracy instead.

5. a == b on objects when you meant content equality

✗ WRONG

Using == on two String/object variables to check if their contents match.

✓ RIGHT

== checks the reference; a.equals(b) checks the content. Know which one your test actually needs.

6. Confusing "illegal input" with "illegal set of values"

✗ WRONG

Treating {2,3,8} (impossible triangle) the same as -3 or "not integer" (illegal format).

✓ RIGHT

They're different flavors: illegal set of values = wrong format/negative; illegal input = values that are individually fine but together violate a domain rule (impossible condition, like the triangle inequality); totally bad input = wrong type entirely (text vs. numbers).

7. Stuffing many asserts into one test method

✗ WRONG

One giant test method with five asserts chained together.

✓ RIGHT

If the first assert fails, the rest never run — you won't know if a later assertion would also have failed. Prefer few (likely 1) asserts per test, and 10 small tests over 1 test that's 10x as large.

8. Comparison-based oracle assumed to scale to any suite size

✗ WRONG

Assuming you always need a predicted output for every input, even for 10,000 auto-generated test cases.

✓ RIGHT

Comparison-based oracles fit small, hand-written suites. Large, auto-generated suites usually need self-checking code instead — which is often only a partial check, but scales.

§ 14 — POP QUIZ

Quick self-check

Click any question to reveal the answer.

1. Does testing improve software?

Reveal
No. Testing only helps identify problems you might choose to resolve. Making the product better is part of a larger process the developer performs after testing — testing itself doesn't fix anything (just like weighing yourself doesn't reduce your weight).

2. Name the five parts of a typical test case table.

Reveal
Name/number, Requirement, Preconditions, Steps, Expected Results. A test case is not valid without an expected result.

3. Short answer: for the triangle problem, give one example each of a boundary value and an "illegal input / impossible condition."

Reveal
Boundary: 0 or MAX_INT as a side length. Illegal input / impossible condition: {2,3,8} or {2,3,5} — these violate the triangle inequality, so no triangle can have those sides.

4. Multiple choice: which of these is the safest way to compare two floats for equality? (a) a == b (b) compare within a tolerance/accuracy limit (c) a.equals(b)

Reveal
(b). Never use == on floats — rounding error means 4.0000000, 3.9999999, and 4.0000001 may all "mean" 4.0. .equals() is for object content comparison, not float precision.

5. What's the difference between a test stub and a test driver?

Reveal
A driver is a "main" program that drives the code under test through its test cases (it calls in). A stub is a substitute for a called function/method/object that the code under test depends on (it's called by the code being tested).

6. True or False: a comparison-based oracle is the only way to automatically judge pass/fail.

Reveal
False. Self-checking code is another approach — the program judges its own correctness (e.g. structural invariants) without needing a predicted output, though it's often only a partial check.

7. Why is a smoke test not a substitute for functional testing?

Reveal
A smoke test is just a representative subset of test cases — good for confirming a build deploys cleanly or is ready to send to test, but it doesn't exercise the full breadth of functionality a real functional pass would.

8. Why should a test method have "few (likely 1)" assert statements?

Reveal
Because the first failing assert stops the method — later asserts never run, so you can't tell whether they would also have failed. Fewer asserts per test keeps failure diagnosis precise.
§ 15 — PRACTICE LAB

Apply the coverage angles

Realistic scenario questions grounded in the deck's own triangle and search-and-replace examples.

1. You're testing a function that classifies a triangle from three integer side lengths. List one test input for each: a valid case, a boundary case, and an "impossible condition" case.
→ Valid: {3,4,5} (scalene). Boundary: side length 0 or MAX_INT. Impossible condition: {2,3,8} (fails the triangle inequality).
Why: this directly mirrors the deck's three coverage angles — valid input, boundary conditions, and illegal input (impossible conditions) — applied to the same example used in lecture.
2. A teammate writes this test case: "Steps: enter some text into the username field. Expected results: it should probably work." What's wrong with it, and how would you fix it per the deck's rules for writing test cases?
→ Not data-specific ("some text") and expected result is vague ("probably work"). Fix: specify the exact input (e.g. "enter shoug_a") and a precise, verifiable expected result (e.g. "account is created and the dashboard loads").
Why: test cases must be repeatable and data-specific, and every test case must have a concrete expected result — this is exactly the good-vs-bad TC-47 contrast from the slides.
3. You need to run 5,000 auto-generated test cases overnight with no human involvement in checking results. Which type of oracle fits better, and why?
→ Self-checking code, generally — a comparison-based oracle needs a hand-predicted output for every one of the 5,000 inputs, which doesn't scale; a self-checking oracle can judge correctness (even if only partially) without predicted outputs.
Why: "no use running 10,000 test cases automatically if the results must be checked by hand" — comparison-based oracles fit small, hand-generated suites; large auto-generated suites lean on self-checks.
4. Explain, using the "weighing yourself" analogy, why "we ran 500 test cases and they all passed" does not by itself mean the software is high quality.
→ Running tests only reveals whether problems exist within what was tested — it doesn't fix anything and doesn't guarantee untested behavior is correct. Quality comes from the full process (design, coding, fixing found defects), not from the act of testing itself.
Why: "testing does not make the product better, even though it's part of a process that does" — the deck's opening and most-tested conceptual point.