What is a test, in plain words?
A test is a small piece of code that runs your real code and checks the answer. You give it an example where you already know what should happen, such as “one sourdough loaf at 4.50 costs 4.50”, and it tells you whether the code agrees. If it does, the test passes. If it doesn’t, the test fails and shows you both numbers.
Tests are ordinary JavaScript. The file on the left, menu.test.js, calls total() exactly the way the rest of the bakery site would, then uses expect() to say what the result should be. A test runner finds every test, runs them all, and reports which passed. On this page the runner works in your browser and re-runs everything each time you stop typing.
You have probably tested code before without calling it that: refreshing the page, clicking a button and looking at what appears. A written test does the same checking, except it never gets tired, never forgets a case, and takes a fraction of a second.
Why bother, if the code already seems to work?
Because “seems to work” usually means “worked for the one example I tried”. The starting code is a good illustration. With one loaf in the basket, total() gives the right answer, and the first test passes. With two items it gives the wrong answer, because line 5 replaces the running total instead of adding to it. Nobody would spot that by trying a single loaf.
Tests also protect you from the future. Code changes all the time: a new discount, a rounding fix, a rename suggested by an AI tool. Each change can break something that used to work, without any error to tell you. That kind of bug has a name, a regression, and a good set of tests catches regressions within seconds of the change that caused them.
There is a third benefit that people notice later. Tests are examples of how your code is meant to be used, written in a form that is checked every time. When you come back to a file months later, the tests tell you what each function promises.


What does each line of the first test do?
import { test, expect } from 'vitest' brings in the two tools you need. test() defines one test: its first argument is a sentence describing what should happen, and its second is a function containing the check. Write the sentence with care, because it is what you read in the Results view when something fails.
expect(total(...)) runs the code and wraps the result. .toBe(4.5) is a matcher: it compares the result with the value you expect. If they are the same, nothing happens and the test passes. If they differ, the matcher throws an error that the runner catches and turns into a red cross with Expected and Received underneath.
import { test, expect } from 'vitest';
import { total } from './menu.js';
test('one sourdough loaf costs 4.50', () => {
expect(total([{ name: 'Sourdough', price: 4.5, qty: 1 }])).toBe(4.5);
});How do you read a failing test?
Add a test with two items and the Results view turns red. Read it from the top: the test’s name tells you which situation failed, the message says what the matcher wanted, and the Expected and Received boxes show the two values side by side. For a loaf at 4.50 and a croissant at 2.10, Expected is 6.6 and Received is 2.1, the price of the last item alone.
That single clue points straight at the bug. If the answer is always the last item, the loop must be throwing away what came before. Line 5 says sum = item.price * item.qty, which replaces the sum each time round. Changing = to += makes it add instead.
Fix the code, and the test goes green. That small loop, write a test, see it fail, change the code, see it pass, is the heart of everything in this course.
How does Check my work know your tests are good?
A test that always passes proves nothing. To see whether your tests would really catch problems, Check my work runs them a second time against broken copies of the code. One copy has the original bug back. Another ignores the quantity, so three loaves cost the same as one. If your tests pass against a broken copy, they missed that bug, and the check tells you which kind of test is missing.
Professional teams use the same idea under the name mutation testing: change the code in small, deliberate ways and see whether the tests notice. You will meet it again in the coverage lesson. For now it is enough to know that a good test fails when the code is wrong, as well as passing when it is right.
What are Vitest and Jest?
They are the two most widely used JavaScript test runners. Jest has been around longer and is still everywhere. Vitest is newer, faster in projects that use modern build tools, and copies Jest’s style closely, so test, expect and most matchers are the same in both. This course uses Vitest’s names, and the practice runner also accepts Jest’s, such as jest.fn().
On your own computer you would install one with npm, put tests in files ending in .test.js, and run npx vitest. It watches your files and re-runs the tests on every save, just like the Results view here. The practice runner supports the features the lessons teach and runs entirely inside this page, with nothing installed and nothing sent anywhere.
What makes a test worth having?
Three things, mostly. It checks one clear situation, so a failure points at one problem. It uses an example where you know the right answer without running the code, so you are not copying whatever the code currently does. And it would fail if the code were wrong, which sounds obvious but is easy to get wrong, as the Break It challenge shows.
Which tests you choose counts for more than how many you write. Two or three tests that cover different kinds of basket, empty, one item, several items, a quantity above one, catch far more bugs than twenty tests of the same single loaf.
- Name the situation in plain words
- Work out the expected answer yourself
- Make sure the test would fail if the code broke
- Cover different kinds of input, not the same one many times
Do tests replace trying the site yourself?
No, they work alongside it. Tests are excellent at checking rules: totals, dates, validation, what appears after a click. They are poor at judging whether something looks right, reads well or feels pleasant to use. You still open the page, try it on a phone and ask real people.
What tests change is how often you need to repeat the boring checks. Once a rule has a test, you never have to check it by hand again, and you find out the moment it breaks.
Field notes
- A test runs your code with an example and checks the answer you expect.
- Code that works for one example can still fail for others.
- When a test fails, compare Expected and Received to find the clue.
- A good test fails when the code is wrong and passes when it is right.
Easy to get wrong
Running the code is not the same as checking it. A test without expect() passes whatever the code returns, so it can never catch a bug. Every test needs at least one assertion.
If you paste in whatever the code currently returns, the test just records the bug. Decide the right answer from the rules, then let the test compare.
Bugs hide in the situations you didn’t try. The starting code passes the one-item test and fails every basket with more than one line.
Questions people ask
Do I need to install anything for this course?
No. The tests run in this page, in a practice runner that follows Vitest’s style. On your own projects you would install Vitest or Jest with npm, and the tests you write here would look the same there.
Is testing only for big professional projects?
No. A handful of tests helps on a small site too, especially around anything with money, dates or forms. The more often you change your code, the more tests pay for themselves.
Why does the runner say 8.4 when I expected 8.40?
Numbers in JavaScript have no trailing zeros, so 8.40 and 8.4 are the same value and toBe(8.4) matches either way. Formatting a price as text with two decimals is a separate job, usually done with toFixed(2), and can have its own test.
Can an AI tool write my tests?
It can draft them, and it can also write tests that check nothing or that copy a bug into the expected value. The last lessons cover how to review AI-written tests. The Break It challenge here shows two of the most common problems.