Advanced · Lesson 9/9

Testing & Performance — Ship with Confidence

30 min 2 exercises 1 quiz

What you'll learn

  • What automated tests actually are (you'll write a tiny test tool yourself)
  • The testing pyramid every team talks about
  • Mocking — testing in isolation without a real server or database
  • Core Web Vitals — the 3 numbers Google grades your site with

Fun fact: every exercise you've passed in this course was checked by automated tests. Today you look behind the curtain.

The taste-tester 👨‍🍳

Imagine a restaurant where, after every change to any recipe, a robot instantly tastes every dish and yells if something went bad. You'd never accidentally ruin the soup by changing the salt jar.

That's automated testing. A test is just code that:

  1. Runs your function with known inputs
  2. Compares the result to what it should be
  3. Screams if they don't match
// the function
function add(a, b) { return a + b; }

// the test
if (add(2, 3) !== 5) {
  throw new Error("add is broken!");
}

Real projects use a test runner like Jest or Vitest — same idea with nicer tools:

test("adds two numbers", () => {
  expect(add(2, 3)).toBe(5);
});

The pyramid: many fast unit tests (one function), some integration tests (parts working together), few end-to-end tests (robot clicks through the real site). Why tests matter for YOUR career: they're what lets teams change code without fear — and interviewers ask about them.

Exercise 1

Build your own test tool! Write a function expectEqual(actual, expected) that:

  1. Prints "PASS" with console.log when they are equal (===)
  2. Prints "FAIL expected <expected> got <actual>" when they differ

Then use it to test the provided multiply function — one passing case and one deliberately failing case are already written for you.

Arrays and objects need a different check ⚠️

Your expectEqual uses ===, which works perfectly for numbers and strings. But try comparing two arrays with it:

[1, 2, 3] === [1, 2, 3]   // false! Even though they "look" the same

=== checks identity (are these the exact same object in memory?), not contents. Two separately-created arrays are never the same object, even with identical items. Real test tools solve this by comparing contents deeply — the simplest trick is turning both into strings first:

JSON.stringify([1, 2, 3]) === JSON.stringify([1, 2, 3])   // true!

This is exactly why several exercises in this course compare arrays using JSON.stringify(...) === JSON.stringify(...) inside their checks — now you know why, instead of just copying the pattern.

Exercise 2

Write expectArrayEqual(actual, expected) — like expectEqual, but correct for arrays. Print "PASS" if their contents match, otherwise "FAIL".

Mocking — testing without the real thing 🎭

What if your function calls a real API or database? You don't want tests hitting a live server every time (slow, costs money, fails if the internet is down). Mocking replaces the real dependency with a fake stand-in that returns known data instantly:

// real version — calls an actual API
function getUser(id) {
  return fetch("/api/users/" + id).then(r => r.json());
}

// mocked version, used only during tests
function mockGetUser(id) {
  return Promise.resolve({ id: id, name: "Test User" });
}

The test calls mockGetUser instead of the real one, so it runs in milliseconds, never touches the network, and always returns the same predictable data — perfect for checking "does my component display the user correctly?" without caring whether the real API works today.

Real-world case study: this exact course 🔍

You don't need to imagine how automated testing works — you've been inside it this entire course. Every exercise you've passed has a hidden check string, like add(2,3) === 5, that runs against your code the instant you click "Check" — that is expectEqual, just wired into this platform's UI instead of printing to a console. When your code passes all checks, the lesson marks itself complete automatically. You weren't just learning about tests in this lesson; every lesson before it was testing you the same way.

Performance — Core Web Vitals ⚡

Google measures every site with three numbers (they affect your search ranking!):

  • LCP (Largest Contentful Paint) — how fast the main content appears. Target: < 2.5s
  • INP (Interaction to Next Paint) — how fast the page reacts to clicks. Target: < 200ms
  • CLS (Cumulative Layout Shift) — does content jump around while loading? Target: < 0.1

Check any site now: open Chrome DevTools (F12) → Lighthouse tab → Analyze. You get a 0–100 score and a fix-list.

The usual junior-level wins: compress/resize images (biggest one by far), set width/height on images (kills CLS), lazy-load below-the-fold content, and ship less JavaScript.

Quick quiz

Question 1 of 6

What is a unit test?

Question 2 of 6

expect(add(2,3)).toBe(5) — what happens if add returns 6?

Question 3 of 6

Your page's images pop in and push the text down while loading. Which vital suffers?

Question 4 of 6

Where do you run a quick performance audit?

Question 5 of 6

Why does [1,2,3] === [1,2,3] return false?

Question 6 of 6

Why mock a real API call in a test?

Summary

  • Tests = robot taste-testers; you built expectEqual, the seed of Jest's expect().toBe()
  • Arrays/objects need content comparison (JSON.stringify), not ===
  • Mocking replaces real APIs/databases with fast, predictable fakes during tests
  • Pyramid: many unit, some integration, few end-to-end
  • Core Web Vitals: LCP < 2.5s, INP < 200ms, CLS < 0.1 — audit with Lighthouse
  • Every exercise in this entire course has been testing you with exactly this mechanism

Next step on your machine: npm create vite@latest, add Vitest, and write 3 real tests for the add and safeAdd functions from earlier lessons. Then Lighthouse-audit your Next.js site and fix the top finding.