Test the Expected, the Boundary, and the Invalid

Design a small meaningful test set covering normal behavior, important boundaries, and invalid inputs.

By Ian Fang Beginner 25 minutes
A student-centered editorial illustration representing Test the Expected, the Boundary, and the Invalid.

A basic meaningful test set covers three categories: expected use, important boundaries, and invalid input. This does not prove a program correct, but it tests more than one friendly example.

Begin with a specification

Suppose a function calculates the mean of a non-empty list of scores from 0 through 100. It returns a numeric mean and rejects an empty list, nonnumeric values, and values outside the range.

The specification defines the tests. Without it, you may only confirm whatever the current code happens to do.

Test expected behavior

Expected cases represent ordinary valid use:

[80, 90, 100] → 90
[50] → 50

Use values whose result you can calculate independently. One expected case is rarely enough when different valid shapes follow different paths.

Test boundaries

Boundaries sit at or near a rule’s edge:

[0] → 0
[100] → 100
[0, 100] → 50

Other boundaries include empty versus one item, maximum length, dates around a month change, and text at an allowed size. Choose boundaries from requirements, not from a generic list.

Test invalid input

Invalid tests confirm rejection behavior:

[] → clear empty-input error
[101] → clear range error
[-1] → clear range error
["ninety"] → clear type or format error

State what safe failure means. A crash, silent correction, partial output, or exposure of private data is not acceptable merely because the input was invalid.

Build a test table

Category Input Expected result Reason
Expected [80,90,100] 90 Ordinary values
Boundary [0,100] 50 Both valid extremes
Invalid [] Empty-input error Required non-empty
Invalid [101] Range error Above maximum

Run each case and record actual output. Do not mark a test passed without executing it. If an AI generates candidate tests, compare them with the specification and calculate expected results independently.

Add regression tests

When debugging reveals a defect, preserve a minimal test that fails before the fix and passes after it. The test should express expected behavior, not depend on an accidental implementation detail.

Consider privacy and side effects

Use synthetic data unless authorized real data is necessary. For programs that write files, send messages, charge accounts, or change systems, use a practice environment, temporary destination, mock, or dry run appropriate to the project. Verify cleanup and do not test destructive behavior on valuable data.

Know the limits

Three categories are a starting framework, not full coverage. Security, performance, concurrency, accessibility, numerical precision, and integration may require specialized tests. State what the current set does not assess.

Common mistakes

  • Writing expected outputs after running the program.
  • Testing only examples used during implementation.
  • Calling an empty input invalid when the specification permits it.
  • Checking that an error occurred but not which error.
  • Using private production data for convenience.
  • Accepting generated tests without inspecting assumptions.

Do this now

Implement or use a safe score-mean function. Write at least two expected, three boundary, and four invalid cases before changing the implementation. Run them and record actual results.

Log what you learned

Record only:

  • Result: What did the action produce?
  • Evidence: What observation, test, or source supports that result?
  • Next action or unresolved question: What should happen next?

Next, package this evidence into a reproducible bug report when another person needs to help.