Skip to content

Expert · Functional patterns and testing · topic 50 of 61 · 30 min

Testing your code

Explanation

Automated tests are small programs that check your code gives the right answers, so you can change it without fear. Unit tests check one function, integration tests check parts working together, and end-to-end tests drive the real app. In JavaScript you usually write tests with Vitest or Jest (describe, it, expect) or Node's built-in node:test.

Why automated tests?

Checking by hand ("I clicked around and it looked fine") does not scale. When you change one function in a 50,000-line app, you cannot re-check everything by hand. An automated test suite re-checks hundreds of behaviours in seconds, every time, and tells you exactly what broke. Every task in this course is checked by small automated tests.

Kinds of tests

KindChecksSpeedTools
UnitOne function or class aloneMillisecondsVitest, Jest, node:test
IntegrationSeveral parts together (API + database)SecondsVitest/Jest + a test database, Supertest
End-to-end (E2E)The real app in a real browserSeconds to minutesPlaywright, Cypress

The "testing pyramid" advice: many fast unit tests, fewer integration tests, and a small number of E2E tests for the most important user journeys (sign up, checkout).

A first test with Vitest (Jest looks almost the same)

javascript
// price.js
export function applyDiscount(price, percent) {
  if (percent < 0 || percent > 100) throw new RangeError('Bad percent');
  return Math.round(price * (1 - percent / 100));
}

// price.test.js
import { describe, it, expect } from 'vitest';
import { applyDiscount } from './price.js';

describe('applyDiscount', () => {
  it('takes the percentage off', () => {
    expect(applyDiscount(1000, 10)).toBe(900);
  });
  it('rounds to the nearest rupee', () => {
    expect(applyDiscount(999, 15)).toBe(849);
  });
  it('rejects impossible percentages', () => {
    expect(() => applyDiscount(100, 120)).toThrow(RangeError);
  });
});
bash
npm install --save-dev vitest
npx vitest run
Run all *.test.js files.

Arrange, act, assert

A clear test has three parts: arrange (set up the inputs), act (call the code), assert (check the result). One behaviour per test, with a name that reads like a sentence: "rejects impossible percentages". When it fails, the name tells you what broke.

What to test: edge cases

  • The normal case (₹1000 with 10%).
  • Boundaries: 0, 100, exactly at a limit (₹499 for free delivery).
  • Empty inputs: [], '', no arguments.
  • Bad inputs: negative numbers, wrong types, null.
  • Rounding and money: 0.1 + 0.2 style surprises.

node:test, built into Node

javascript
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { applyDiscount } from './price.js';

test('takes 10% off', () => {
  assert.equal(applyDiscount(1000, 10), 900);
});
// run with: node --test

TDD and coverage

Test-driven development (TDD) means writing a failing test first, then the simplest code that passes, then cleaning up (red, green, refactor). Code coverage tools report which lines your tests ran. High coverage is a good sign but not proof: a test can run a line without checking that its result is right.

Careful: A test that never fails is worthless. After writing a test, break the code on purpose (change > to >=) and make sure the test goes red. This course does the same: every task's starter code must fail its tests.
Think of it like this: Tests are like the quality-check counter at the end of a garment factory line. Every shirt is checked for buttons, stitching and size before it ships, automatically, not only when a customer complains.
Tip: When you fix a bug, first write a test that reproduces it (it fails), then fix the code (it passes). That bug can never silently come back.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Testing your code · 1/6

Testing

In this lesson you will write automated tests that check your code for you.

Prefer a video by a teacher? Find free videos on “javascript unit testing vitest jest tutorial for beginners” on YouTube ↗

In real life: Testing GST rules before a rate change

Before a government rate change, an e-commerce company's finance team updated their tax function. The test suite of 120 cases (different categories, interstate vs intrastate, exempt items, rounding) ran in two seconds and found that the new code mis-rounded one category. It was fixed before any invoice went out wrong.

javascript
function assertEqual(actual, expected, msg) { if (actual !== expected) throw new Error(`${msg}: expected ${expected}, got ${actual}`); }
const applyDiscount = (price, pct) => Math.round(price * (1 - pct / 100));
const cases = [[1000, 10, 900], [999, 15, 849], [500, 0, 500], [500, 100, 0]];
for (const [price, pct, want] of cases) assertEqual(applyDiscount(price, pct), want, `${price} - ${pct}%`);
console.log(`${cases.length} tests passed`);

Practice (0/3 done)

Task 1 · Code · Javascript

Write your own tiny test helper expectEqual(actual, expected, name) that returns "PASS <name>" when actual === expected and "FAIL <name>: expected <expected>, got <actual>" otherwise.

Task 2 · Code · Javascript

freeDelivery(total) should return true when total is at least 499. Write the function AND an array cases of at least 4 [input, expected] pairs that include the boundary values 498, 499 and 500. The test runs your cases against your function and a buggy version; your cases must catch the bug.

Task 3 · Practice

On your computer: create a folder, run npm init -y and npm install --save-dev vitest, write sum.js exporting sum(list) and sum.test.js with at least 3 tests (normal, empty list, negative numbers), and run npx vitest run. Then break sum on purpose and see a test fail.

Quick quiz

Question 1

1.Which test kind should you have the MOST of?

Question 2

2.What are the three parts of a clear test?

Question 3

3.What does 100% code coverage prove?

Question 4

4.In TDD, what do you write first?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

Which test kind should you have the MOST of?

Answer: Unit

Unit tests are fast and pinpoint failures; the pyramid has them at the base.

What are the three parts of a clear test?

Answer: Arrange, act, assert

Set up, call the code, check the result.

What does 100% code coverage prove?

Answer: Every line was run by some test

Coverage shows lines executed, not that results were checked correctly.

In TDD, what do you write first?

Answer: A failing test

Red (failing test), then green (code), then refactor.

What does Arrange-Act-Assert mean?Related

Answer: Set up the data, call the code, check the result

It is the simple shape of almost every test.

From Testing basics: unit, integration and end-to-end

In Arrange, Act, Assert, what happens in the Act step?Related

Answer: You call the code being tested

Arrange prepares, Act calls the function once, Assert checks the result.

From Unit testing: checking one piece at a time

What does Arrange-Act-Assert describe?Related

Answer: The structure of a test: set up, call, check

It keeps tests readable.

From Unit testing with xUnit

What does an integration test check that a unit test does not?Related

Answer: That several parts, like handler and database, work together

Integration tests check the joins between parts, where many real bugs live.

From Integration testing: checking parts work together

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 1 more topic in “Functional patterns and testing” before its exam: Test doubles and testable design.
  2. Module 10 exam: Functional patterns and testing · 12 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, Performance and Node.js. 61 topics left in the course before the final exam.

← Previous: Memoization, debounce and throttle