Cover different reasons to fail
A grade average accepts numeric grades from 0 through 20, returns null for an empty array and rejects invalid inputs. Separate normal, boundary, absent and malformed cases rather than listing many near-identical examples.
| Case | Expected behavior |
|---|---|
| [12, 16] | 14 |
| [0, 20] | 10 |
| [] | null |
| ["12"] | Error |
| [-1] | Error |
| null | Error |
Use the validated calculateAverage function from the defensive programming module. In your test file import it from calculations.mjs along with Node's strict assert module.
import assert from "node:assert/strict";
import { calculateAverage } from "./calculations.mjs";
assert.equal(calculateAverage([12, 16]), 14);
assert.equal(calculateAverage([0, 20]), 10);
assert.equal(calculateAverage([]), null);
assert.throws(() => calculateAverage(["12"]));
assert.throws(() => calculateAverage([-1]));
assert.throws(() => calculateAverage(null));
Guided refactor
Replace a reduce-based sum with a for...of loop, preserving validation and empty-input behavior. Rerun the same cases. Passing tests provide evidence that these behaviors survived; inspect the change too, because the suite may not cover everything.
For decimal results, use an appropriate tolerance or a domain rounding policy rather than assuming every floating-point fraction has exact binary representation. Integer-cent money calculations still need explicit rounding when applying rates.
Independent exercise
Add a case that verifies the average function does not mutate the supplied array. Then extend the policy to allow a grade of 20.5. Which expectations must be reviewed?
Correction
Keep a copy of the input, call the function, then deep-compare the original with the copy. A new upper bound changes the validation contract: add 20.5 as accepted and a value just above it as rejected. Do not change the implementation alone while leaving the old policy in the lesson or tests.
Review
A useful regression suite records important decisions. When a rule changes, update requirements, examples and implementation together, and preserve tests for unaffected behavior.