Testing & Performance — Ship with Confidence
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:
- Runs your function with known inputs
- Compares the result to what it should be
- 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.
Build your own test tool! Write a function expectEqual(actual, expected) that:
- Prints
"PASS"with console.log when they are equal (===) - 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.
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.
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.
Practice
Reading is not learning until you run something. Pick one.