█
LastWrite
  • > Curriculum
  • > Pricing
  • > For Educators
  • > About
  • > Contact
Log InGet Started

Questions, concerns, bug reports, or suggestions? We read every message, write to us at [email protected].

More ways to reach us →
LastWrite

Structured computer science lessons for aspiring developers and security professionals.

[email protected]

(201) 785-7951

Mon–Fri, 9 AM–5 PM EST

Learn

  • Curriculum
  • Pricing

Company

  • About
  • For Educators & Schools
  • Contact Us

Legal

  • Terms of Service
  • Privacy Policy
© 2026 LastWrite. All rights reserved.
Curriculum/Software Engineering/Testing/Unit Testing Fundamentals
40 minBeginner

Unit Testing Fundamentals

After this lesson, you will be able to: Write a good unit test using Arrange-Act-Assert, keep tests isolated and deterministic, and use mocking and stubbing only where they belong.

A unit test is the foundation of the pyramid. This lesson is language-agnostic: what makes a unit test good, the structure every test should follow, and the difference between mocks and stubs (asked about constantly in interviews).

Prerequisites:Why Testing Matters and the Testing Pyramid

The anatomy of a good unit test

A good unit test is fast, isolated, repeatable, and tests one behavior. Fast: milliseconds, so you run the whole suite constantly. Isolated: it does not depend on other tests, shared state, the network, the clock, or the filesystem. Repeatable: it passes or fails the same way every time (no flakiness). Focused: one logical assertion of one behavior, so when it fails you know exactly what broke. A test that needs a paragraph to explain what it checks is testing too much.

Arrange-Act-Assert (AAA)

Every unit test has the same three-part shape. Keep them visually separated.

tsx
// Arrange: set up the inputs and the system under test
const cart = new Cart();
cart.add({ id: 'a', price: 10 });
cart.add({ id: 'b', price: 5 });
// Act: perform the ONE action you are testing
const total = cart.totalWithTax(0.10);
// Assert: check the outcome
expect(total).toBe(16.5);
// One behavior per test. If you find yourself asserting five unrelated
// things, that is five tests wearing a trench coat. Split them.

Mocks, stubs, and fakes (the interview question)

A test double replaces a real dependency. A stub returns canned data so the code under test can run (a fake getUser that always returns the same user). A mock additionally verifies HOW it was called (assert that sendEmail was called exactly once with this address). A fake is a lightweight working implementation (an in-memory database). Rule of thumb: stub queries (things that return data), mock commands (things with side effects you need to verify), and prefer a real or fake implementation over a mock whenever it is cheap, because over-mocking produces tests that pass while the real system is broken.

💡 Test behavior, not implementation

Assert on what the caller observes (the return value, the visible effect), never on private internals. A test that breaks when you rename a private method without changing behavior is testing the wrong thing. The single biggest cause of brittle test suites is coupling tests to implementation details.

Quick Check

You need to test that a function sends a welcome email after signup. What test double fits the email service?

Pick the best choice.

Sign in and purchase access to unlock this lesson.

Sign in to purchase
←Why Testing Matters and the Testing Pyramid
Back to Testing
Unit Testing in JavaScript and TypeScript→