A test is a repeatable program that fails when an expectation is violated. Printing true for each comparison helped you learn the pattern, but a test runner should not require someone to inspect every line manually. A small check helper can throw AssertionError when its condition is false and let a successful run print one final confirmation.
This course uses ordinary methods instead of introducing a testing framework. AssertionError here is explicitly constructed and thrown; it does not depend on Java’s optional assert keyword or on a command-line flag. Do not catch the test failure and print All passed anyway. A failed test should remain visibly failed.
Choose cases from the contract
A boundary check should distinguish a plausible defect: validMinutes(1) catches using > rather than >=, and validMinutes(0) catches forgetting the lower bound. For mutable objects, check both the result of an operation and the state afterward. For recovery code, show that an invalid attempt did not prevent a later valid one.
A regression check records a bug you have reproduced so the same mistake is noticed if it comes back. First run a known-correct implementation. Then introduce one deliberate defect, confirm that an existing check fails, and restore it. Passing tests are evidence for the cases and properties you chose, not a proof that every possible input or future change is safe.
public class Main {
public static void main(String[] args) {
check("lower bound", validMinutes(1));
check("reject zero", !validMinutes(0));
check("upper bound", validMinutes(240));
check("reject above", !validMinutes(241));
System.out.println("Duration checks passed");
}
static boolean validMinutes(int minutes) {
return minutes >= 1 && minutes <= 240;
}
static void check(String label, boolean condition) {
if (!condition) {
throw new AssertionError(label);
}
}
}Check your prediction
Duration checks passed Change >= 1 to > 1 and rerun: the lower-bound check throws and the final success message is not printed. A test that cannot notice the defect it claims to guard is weak evidence.
public class Main {
public static void main(String[] args) {
Counter counter = new Counter();
check("reject negative", !counter.add(-1));
check("unchanged", counter.current() == 0);
check("accept positive", counter.add(3));
check("updated", counter.current() == 3);
System.out.println("Counter checks passed");
}
static void check(String label, boolean condition) {
if (!condition) {
throw new AssertionError(label);
}
}
}
class Counter {
private int value;
boolean add(int amount) {
if (amount < 0) {
return false;
}
value += amount;
return true;
}
int current() {
return value;
}
}Check your prediction
Counter checks passed Removing the validation breaks the first check. Moving validation below mutation can break the unchanged-state check. The later successful operation verifies that a rejection did not disable the counter.
Your turn
Test a returned calculation
Write nonnegativeDifference(planned, completed) for nonnegative int values, which returns planned-completed but never below zero. Use check to test unfinished, exactly finished, exceeded and zero/zero inputs. Print All difference checks passed only after every check succeeds.
public class Main {
public static void main(String[] args) {
// Make four checks, then print a success line.
}
// Define nonnegativeDifference and check.
}Hint 1: the idea
Tests should include both the normal subtraction and the branch that clamps an exceeded plan.
Hint 2: a first step
Give each check a label that identifies the rule. Throw when a condition is false rather than printing a success message in both branches.
Show one solution
public class Main {
public static void main(String[] args) {
check("unfinished", nonnegativeDifference(30, 10) == 20);
check("finished", nonnegativeDifference(30, 30) == 0);
check("exceeded", nonnegativeDifference(30, 40) == 0);
check("zero", nonnegativeDifference(0, 0) == 0);
System.out.println("All difference checks passed");
}
static int nonnegativeDifference(int planned, int completed) {
if (completed >= planned) {
return 0;
}
return planned - completed;
}
static void check(String label, boolean condition) {
if (!condition) {
throw new AssertionError(label);
}
}
}All difference checks passed Change it and explain
Delete the clamping branch and confirm which labeled check fails. Restore it, then change the unfinished expectation to an incorrect value and inspect that failure too. The test is code and can be wrong; always return to the contract before deciding which side to change.
The project test file will run as another entry point. It calls the same model classes as the normal program, but supplies its own cases instead of reading input. Separating input from model behavior makes this reuse possible without adding fake console typing to every object test.
Completion is saved in this browser only. It does not measure mastery.