Your First Tests With Vitest
Set up Vitest, write four tests for cart math, break one on purpose and read the failure. After this, you can check your money math in milliseconds instead of by clicking.
Course content
Sign in to save your progress.
-
Set Up Projects That Don't Break Each Other
-
Node.js and npm: Running JavaScript Outside the Browser
-
package.json and Lock Files: Same Install Every Time
-
-
-
Organize Code Other People Can Read
-
JavaScript Modules: import, export and Splitting One Big File
-
JavaScript Classes: When an Object Literal Is No Longer Enough
-
Destructuring and Spread Without the Confusion
-
-
-
Work With Real Data
-
Array Methods: map, filter, reduce and When a Loop Is Clearer
-
Working With JSON: parse, stringify and What Breaks
-
Dates in JavaScript Without Losing a Day
-
-
-
Waiting for Things
-
Callbacks to Promises: Why the Code Runs Out of Order
-
async/await and fetch: Calling a Real API
-
-
-
Prove It Works Before Someone Else Finds Out
-
try/catch and Custom Errors
-
Your First Tests With Vitest
-
-
- Final Check: JavaScript Intermediate
Skip ahead: If you already have Vitest running with npm test, skip to "Reading a failing test".
Every time you change the discount code, you open the page, add two items, apply a code, and check the total by eye. That's a test. It's just slow, and you'll skip it on the day it matters. An automated test does the same check in milliseconds, every time, and it doesn't get bored. This lesson sets up Vitest, writes four tests, and breaks one on purpose so you can read the failure.
Why Vitest
There are several good test runners for JavaScript. Jest is the long-time standard and you'll find it in many existing projects. Vitest uses almost the same describe / it / expect style, works with import/export without extra setup, and is fast. For a new project, I'd pick Vitest. For an existing project that already uses Jest, keep Jest. Switching runners is not a good first task.
Setup
In an empty folder:
npm init -y
npm pkg set type=module
npm pkg set scripts.test="vitest run"
npm install --save-dev vitest
That installs Vitest as a dev dependency, makes .js files ES modules, and sets npm test to run all tests once. (Plain npx vitest without run stays open and reruns tests whenever you save, which is nice while you work.) The examples here were run with Vitest 5.0.4 on Node 24.
The code to test
cart.js:
export function subtotal(items) {
return items.reduce((sum, item) => sum + item.price * item.qty, 0);
}
export function applyDiscount(amount, percent) {
if (percent < 0 || percent > 100) {
throw new RangeError(`Discount must be 0 to 100, got ${percent}`);
}
return Math.round(amount * (100 - percent)) / 100;
}
The tests
Vitest finds files ending in .test.js (or .spec.js). Put this in cart.test.js, next to cart.js:
import { describe, it, expect } from "vitest";
import { subtotal, applyDiscount } from "./cart.js";
describe("subtotal", () => {
it("adds price times quantity for every item", () => {
const items = [
{ name: "Coffee", price: 180, qty: 2 },
{ name: "Bread", price: 65, qty: 1 },
];
expect(subtotal(items)).toBe(425);
});
it("is 0 for an empty cart", () => {
expect(subtotal([])).toBe(0);
});
});
describe("applyDiscount", () => {
it("takes 10% off", () => {
expect(applyDiscount(425, 10)).toBe(382.5);
});
it("rejects a discount over 100%", () => {
expect(() => applyDiscount(425, 150)).toThrow(RangeError);
});
});
describegroups tests for one function.itis one test. Write the name as a sentence about behaviour, so a failure reads like a bug report.expect(actual).toBe(expected)is the check. UsetoEqualfor arrays and objects (it compares contents),toBefor numbers, strings and booleans.toThrowneeds a function that throws, so wrap the call:() => applyDiscount(425, 150). Without the wrapper, the error is thrown beforeexpecteven runs.
Run npm test:
RUN v5.0.4 /tmp/cart
Test Files 1 passed (1)
Tests 4 passed (4)
Reading a failing test
A test you've never seen fail might not be testing anything. So break the code on purpose. Change the discount line to Math.round(amount * percent) / 100 and run again:
FAIL cart.test.js > applyDiscount > takes 10% off
AssertionError: expected 42.5 to be 382.5 // Object.is equality
- Expected
+ Received
- 382.5
+ 42.5
❯ cart.test.js:20:36
18| describe("applyDiscount", () => {
19| it("takes 10% off", () => {
20| expect(applyDiscount(425, 10)).toBe(382.5);
| ^
Tests 1 failed | 3 passed (4)
Read it in order: which test (applyDiscount > takes 10% off), what it expected (382.5), what it got (42.5), and the line. 42.5 is 10% of 425, so the code applied the discount instead of the remainder. Put the line back, run again, all four pass.
⚠ Warning: Don't "fix" a failing test by changing the expected value to whatever the code returned. That turns the test into a recording of the bug. First decide which one is wrong, the code or the test. If you can't tell, ask whoever knows the business rule.
What to test first
You don't need 100% coverage. Start with:
- Money math: totals, discounts, tax, rounding.
- Rules that block bad input: negative quantity, invalid email.
- Bugs you've already fixed once. Write a test that fails with the bug, then fix it, so it can't come back quietly.
- Edge cases: empty list, zero, one item, the maximum.
💡 Tip: Keep tests fast and independent. A test shouldn't depend on another test running first, on today's date, or on a real API. If code needs a date, pass the date in as an argument so the test can choose it.
Common mistakes
- Using
toBeon objects or arrays (two arrays with the same items are not the same array). - Calling the function inside
expectfortoThrowwithout a wrapper. - Testing private details instead of results, so every refactor breaks the tests.
- Writing tests that hit a real API and fail when the internet is slow.
- Never seeing a test fail.
What happens if you skip this
Every change becomes a small gamble. You fix the discount for one promo and break rounding for another, and you find out when finance asks why the totals are off by a few centavos on a few hundred orders.
What's one calculation in your current project that would cost real money if it were wrong? Write its first test today.
About
Set up Vitest, write four tests for cart math, break one on purpose and read the failure. After this, you can check your money math in milliseconds instead of by clicking.
- Duration
- 5 min
- Last update
- 08/28/2026
Comments (0)
Sign in to commentNo comments yet.