Testing & Quality Engineering

Begin with one three-case function test, then add one evidence boundary at a time. Twelve chapters build a complete testing model from oracles, partitions, units, failures, state machines, properties, integration contracts, controlled nondeterminism, concurrency, mutation sensitivity, suite diagnosis, and recoverable acceptance without assuming pytest, Hypothesis, coverage services, CI accounts, containers, databases, networks, clouds, or production access.

Course details and reading size
Tutorial
Reading comfortAdjust lesson text without changing code or interface size.

Your first trustworthy test: behavior, oracle, and evidence

Objective Run three hand-checkable cases against one price rule and separate the behavior under test from its oracle and runner.

Core explanation

A test is a controlled comparison between observed behavior and an oracle: the rule that decides what should be true. Arrange establishes explicit input and state, act invokes one public behavior, and assert compares the result or effect with the oracle. A test case is valuable when a plausible defect would make it fail; merely executing a line is not enough. The first program tests a discount expressed in integer cents so its arithmetic is hand-checkable. Three cases cover no discount, an ordinary discount, and the total-discount boundary. A separate invalid input proves the declared failure category. The tiny runner counts completed cases only after their assertions pass and prints stable evidence. It proves this function under four inputs in Python 3.9+, not every integer size, currency policy, framework, service, or production path.

Name the behavior, input, oracle, observation, and unproved boundary before counting a test as evidence.

Define the claim before writing test code

A behavior claim names a starting state, one public action, and an observable result. “The discount works” is too vague because it does not identify input units, allowed percentage, rounding, failure, or what a caller can inspect. A useful claim says that 8,000 integer cents discounted by 25 percent returns 6,000 cents, performs no external effect, and reports invalid percentages through one declared category.

Write the claim in ordinary language and calculate the expected result independently before looking at implementation branches. This prevents the current code from silently becoming its own specification. If product policy is unclear, mark the case as an unanswered contract question rather than inventing a convenient expected value and later treating it as authoritative.

Separate fixture, action, oracle, and runner

The fixture supplies controlled inputs and state. The action invokes the public behavior once. The oracle decides correctness, while the runner orders cases, catches assertion failures, reports identity, and returns a terminal status. Combining all four roles in a large helper can hide which value was arranged, which operation ran, and why a comparison is trusted.

A test framework can improve discovery and reporting, but it cannot supply the missing domain oracle. The Chapter 1 loop deliberately keeps each tuple and calculation visible. When migrating to unittest or pytest, preserve the same case identity and exact assertion rather than replacing them with a vague smoke test that merely completes without an exception.

Read a failed assertion as evidence

A failure should expose the smallest safe values needed to reproduce the divergence: case identity, input, expected result, actual result, and relevant environment identity. It should not dump credentials, entire objects, random memory addresses, or unrelated global state. The tuple attached to the equality assertion is intentionally bounded and deterministic.

The first divergent assertion is usually more useful than the largest traceback. Preserve it before editing several files or rerunning under a different environment. Reduce the case only if the smaller input retains the same behavior boundary, then repair the implementation or oracle according to the written contract and rerun the unchanged baseline.

Laboratory: kill one plausible discount defect

Run the baseline with Python 3.9 or newer and capture exact stdout, stderr, status, and source identity. Seed one defect that uses 99 instead of 100 in the formula, predict the first case that distinguishes it, and retain expected versus actual cents. Restore the formula before adding any new case.

Next change the upper guard from greater than 100 to greater than or equal to 100. The total-discount boundary must now fail even though the ordinary case may pass. Acceptance requires two independently detected defects, a restored three-case baseline, the invalid-input category, empty stderr, status zero, and an explicit statement that service and production behavior remain unproved.

CURRICULUM CONTEXTRelated courses and the course concept model