Define the observable contract
Input partitioning groups values according to behavior the requirement distinguishes. For a quantity accepted from one through four, useful regions include values below the minimum, values inside the valid range, and values above the maximum. Boundaries deserve special attention because an incorrect comparison can classify a whole middle region correctly while rejecting its first or last valid value. The goal is not to collect arbitrary numbers; it is to select examples that would expose different plausible mistakes. A small deliberate partition table often teaches more than a long undirected list of ordinary inputs.
Walk through the real program
The table supplies negative one and zero as rejected values, one and four as accepted boundary values, and five as an excessive value. Each expected boolean is written directly beside the input. Allowed compares the input with its inclusive bounds. Count then totals the rows where actual and expected agree, producing passed:5/5. The result means these five chosen cases agree with the rule. It is not a proof about every integer, a discovered test project, or evidence that a real application validated parsing, null input, capacity updates, or other requirements outside this predicate.
Why the design is testable
Choose representative points for a reason. Negative one and zero are both below this minimum, but zero is a particularly common empty-quantity boundary. One and four test inclusion at the edges. Five sits just outside the upper edge. A value such as two can be added as an ordinary interior case, but repeating many interior values should not replace the boundary checks. When several parameters interact, partitioning each one independently may still miss combinations; use the domain rules to identify which paired conditions or state-dependent boundaries deserve additional cases.
A boundary that matters
A test table should not calculate its Expected column by calling Allowed or by copying the same boolean expression. That would test the implementation against itself. Also do not quietly modify the acceptance limits while keeping old scenario names; the names and expectations should describe the current contract. A percentage, an array index, and a purchase quantity can have different inclusive endpoints even when they all look like integer ranges. Reusing a familiar boundary pattern without reading the requirement can make a thorough-looking set of cases consistently wrong.
Practice: isolate one behavior
First change the implementation's upper comparison to strictly less than four without changing the expected table. Predict one disagreement: the boundary row for four should fail. Hint: the table is valuable precisely because it can reject this plausible mutation. Restore the comparison and add two as an interior value, expecting acceptance. Explain why that additional row improves one part of the evidence but would not have caught the same boundary defect by itself. Keep a written mapping from each case to the mistaken rule it challenges.
Practice: challenge another assumption
For another exercise, change the accepted domain to zero through four, documenting that zero now represents a valid empty request. Update only the cases whose contractual meaning changes, then add a negative value immediately below the new minimum. Hint: an expectation changes because the requirement changed, not because a failing program needs a greener report. Next describe a two-parameter rule involving both requested quantity and available stock. List at least one combination where individually ordinary values produce a rejected result because their relationship violates the rule.
Review the evidence
The five-case baseline is a disciplined sample of the declared input domain. A reviewer should be able to explain every expected boolean without executing Allowed. This is the foundation of meaningful parameterized tests in a full runner, where each row can become its own reported scenario. The portable program deliberately keeps the mechanics visible, while leaving discovery and fixture management to later framework work. Use partitions to find missing questions and boundaries, not to turn a short list of passing inputs into a universal correctness claim.
- The minimum and maximum valid values are both included.
- A wrong strict upper comparison is exposed by the four case.
- Partition evidence applies to the stated rule and tested inputs.
A working example
using System;
using System.Linq;
using System.Collections.Generic;
using System.Globalization;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
bool Allowed(int quantity) => quantity >= 1 && quantity <= 4;
var cases = new[] { (Value: -1, Expected: false), (Value: 0, Expected: false), (Value: 1, Expected: true), (Value: 4, Expected: true), (Value: 5, Expected: false) };
int passed = cases.Count(item => Allowed(item.Value) == item.Expected);
Console.WriteLine($"passed:{passed}/{cases.Length}");
Expected output
passed:5/5
Use cases on both sides of a boundary rather than only middle values.