A test is a function that throws
That is a passing test. Everything a framework adds is about running many of them, not stopping at the first failure, and saying which one broke and why.
function total(line) {
return line.qty * line.price;
}
// the test
if (total({ qty: 2, price: 890 }) !== 1780) throw new Error("total is wrong");
console.log("ok");
Collect, then run
it does not run anything — it registers. The file is read top to bottom, the calls to it fill an array, and then the runner walks it. That is why a console.log at the top of a test file runs before every test, and why a variable defined outside a test is shared by all of them.
const tests = [];
const it = (name, body) => tests.push({ name, body });
async function run() {
const results = [];
for (const test of tests) {
try {
await test.body();
results.push({ name: test.name, ok: true });
} catch (error) {
results.push({ name: test.name, ok: false, error: error.message });
}
}
return results;
}
expect, and a message worth reading
The message is the whole value of a matcher. expect(total(line)).toBe(1780) failing with "expected 1782 to be 1780" tells you what happened; assert(total(line) === 1780) failing with "assertion failed" tells you to go and add a print statement.
function expect(actual) {
return {
toBe(want) {
if (!Object.is(actual, want)) {
throw new Error(`expected ${JSON.stringify(actual)} to be ${JSON.stringify(want)}`);
}
},
};
}
describe, and hooks
beforeEach exists because the alternative — building the fixture at the top of the file — means every test shares one object, and a test that modifies it changes the next one. A hook that runs per test gives each one a fresh start, and the order of the tests stops mattering.
const suites = [{ name: "", tests: [], before: [] }];
const describe = (name, body) => {
suites.push({ name, tests: [], before: [] });
body(); // the body runs NOW, filling the new suite
const done = suites.pop();
suites.at(-1).tests.push(done); // and the finished suite joins its parent
};
const it = (name, body) => suites.at(-1).tests.push({ name, body });
const beforeEach = (fn) => suites.at(-1).before.push(fn);
If your suite passes only in one order, it has hidden shared state — and it will fail the day somebody adds a test in the middle, or the runner parallelises. Building fresh state per test is what prevents it.
What the real ones add
Nothing in that list changes what a test is. It is still a function that throws.
- Discovery — finding
*.test.jswithout you importing them. - Isolation — a fresh module registry per file, and parallel workers.
- Watch mode — re-running only what your change could have affected.
- Better diffs — a coloured, structural comparison rather than two JSON strings.
- Mocking of modules, coverage, snapshots, retries, timeouts.