Academies Claude Code Club ⚙️ Builders · Module 03

Testing your own code.

a seven-step walkthrough · assertions, test cases, real runners · about thirty-five minutes

⚠ The temptation

Ask Claude if your code works.

But Claude will say yes most of the time, even when it's wrong. Tests are how you make Claude prove it. You decide what the test asserts. Claude can only say whether your assertion held.

Step 1 of 7
Step 01 · The trap

You ran it once. It worked. That's not the same as working.

In Skills Workshop Builders 01, you learned to write test cases for Skills. This module is the same idea applied to real code — the code you wrote in Makers, the code you'll write for the rest of your life. And it starts with an uncomfortable moment most kids who learn to code never notice:

You write a function. You try it in the browser. It works on the first try. You smile and close the laptop. Two weeks later you change something unrelated and the function quietly breaks — producing wrong answers instead of error messages. You don't notice for another month. By then you can't remember what you changed.

Every coder has lived this exact story. The cure is called a test — a small piece of code whose only job is to try to catch your other code being wrong. If the test passes, you earned that smile. If the test fails, you found the bug before it found you.

The mindset shift matters more than the technique. A test isn't something you write to confirm your code works. A test is something you write to try to prove it wrong. Tests are suspicious little creatures. That's a feature, not a flaw.

Step 02 · Expect, got

A test has exactly two things.

It doesn't matter what language you use, what framework, what company. Every test in the world boils down to two values sitting side by side: the answer you expect, and the answer the code actually got. If they match, pass. If they don't, fail.

Pass · add(2, 3)

Expected 5

Got 5

✓ expected === got · test passes

Fail · add(2, 3)

Expected 5

Got 23

✗ expected !== got · test fails · the code added as strings instead of numbers

Notice what the second card tells you. Not just "something's wrong" — it tells you exactly what went wrong. You wanted 5. You got "23". That's immediately a clue: somewhere in the code, the numbers are being treated as text and concatenated instead of added. A good test turns a bug into a clue.

When you write a test, the hardest part isn't running it. It's deciding what expected should be. You have to know the right answer before you run the code. That forces you to think clearly about what your function is actually supposed to do — which is half the battle.

Step 03 · Three kinds

Three kinds of test you should always have.

These are the same three categories from Skills Workshop Builders 01 — happy path, edge case, failure — but now written as real JavaScript. A test suite with only happy paths is a lie. A test suite with all three is worth trusting.

1
Happy path

The normal case. Function is called with normal inputs, should return the normal answer. Most tests will be these.

// normal call
test("adds two numbers", () => {
  const got = add(2, 3);
  expect(got).toBe(5);
});
2
Edge case

Inputs that are weird but legal. Zero, negative numbers, empty strings. These are the inputs that usually break beginner code.

// what about zero?
test("adds zero", () => {
  const got = add(0, 5);
  expect(got).toBe(5);
});
3
Failure

Inputs the function shouldn't accept. A good function either throws an error or returns a clear "nope." Most beginners forget this category.

// bad input
test("rejects text", () => {
  const got = add("two", 3);
  expect(got).toBe(null);
});

A good test suite reads like an instruction manual: "here's what this function does, here's how it handles weirdness, here's how it refuses bad input." Future-you opens the tests before the function itself. They're the documentation that can't lie — because the computer checks every time.

Step 04 · Real code

Here's a real test file for a real function.

The function counts how many vowels are in a word. Below it, six tests — two happy paths, two edge cases, two failures. Every test uses the same two-step shape: call the function, check the result.

count-vowels.test.js
// The function we're testing
function countVowels(word) {
  if (typeof word !== "string") return null;
  const vowels = "aeiouAEIOU";
  let count = 0;
  for (const char of word) {
    if (vowels.includes(char)) count++;
  }
  return count;
}

// A tiny test runner — just expect() and test()
function expect(got) {
  return {
    toBe(expected) {
      if (got === expected) return { pass: true };
      return { pass: false, got, expected };
    }
  };
}

// ============ HAPPY PATHS ============

test("counts vowels in a normal word", () => {
  expect(countVowels("hello")).toBe(2);
});

test("counts vowels in a longer word", () => {
  expect(countVowels("encyclopedia")).toBe(6);
});

// ============ EDGE CASES ============

test("empty string returns zero", () => {
  expect(countVowels("")).toBe(0);
});

test("word with no vowels", () => {
  expect(countVowels("rhythm")).toBe(0);
});

// ============ FAILURE CASES ============

test("rejects a number", () => {
  expect(countVowels(42)).toBe(null);
});

test("rejects null", () => {
  expect(countVowels(null)).toBe(null);
});

Read it top to bottom. The real countVowels function lives at the top — about eight lines. Below it, a tiny expect() helper we wrote ourselves (professional tools like Jest do the same thing, just fancier). Then six tests, grouped by kind. The tests are longer than the function. That's normal. Good code is always more tested than written.

Step 05 · The test runner

Write a function. Write tests. Click Run all.

This is a real test runner. The left side has two editable pieces: a function you're testing, and a list of test cases. Click "Run all" and every test runs in a real sandbox — you'll see which ones pass, which ones fail, and exactly what the failing ones got instead.

Live test runner · real assertions, real sandbox
my-function.js
Test results
Click ▶ Run all tests to see results.
Try this: Load the ★ buggy add() demo. Run it. Watch two of the four tests fail. Read the failing tests carefully — they tell you exactly how the code is broken. That's the whole point: turning a mystery into a clue.

Yes — the test runner uses real new Function() sandboxing under the hood. Your function and your tests both run in a real browser JavaScript engine. This isn't a simulation. You're writing real code that really runs.

Step 06 · Test taste

Not all tests are good tests.

A bad test is worse than no test — it lies to you. It says "passing" when your code is broken. Two rounds of judgment.

Round 1. You're testing a function that's supposed to return a user's full name. Which is the better test?

Round 2. You're testing a function that adds two numbers. Which test suite is healthier?

A good test could fail — it asserts something specific enough that a real bug would break it. A good suite has variety — each test covers a different kind of risk. If your tests all look alike, they're probably all catching the same bug and missing the same one. Write tests that try to prove your code wrong. The ones that pass have earned it.

Step 07 · You did it
🧪

You ran real tests against real code.

Not a simulation. Not a walkthrough. Real assertions in a real JavaScript engine. You now share a ritual with every professional coder in the world.

What you just learned

  • "It worked once" is not the same as "it works." The cure is tests.
  • A test is two values: what you expected and what you got. If they match, pass.
  • Three kinds to always have: happy path, edge case, failure.
  • Failing tests turn bugs into clues: they tell you exactly what went wrong.
  • A test that can't fail isn't a test. It's a ritual.
  • Variety > volume. Four different tests beat twenty similar ones.
  • Good code is always more tested than written — the test file is often longer than the function.

In Module 04 — the last Code Club module, the Builders finale — you'll put all of it together to build something other people can actually use: a real tool. Your own small app, with tests, a real URL, and a README. That's the graduation project of Code Club.

★ Before you call it done

Three questions. Same three. Every time.

These are the same three questions for every module in Kindling. They are how you check whether AI did the part it should and you did the part only you could. Tap each one to mark it true.

★ ★ ★

This is yours. Ship it.