Courses / JavaScript / Testing with Jest & Mocha

    ๐Ÿ’ก Running Tests Locally

    Testing frameworks require Node.js. To practice:

    • Install Node.js
    • Jest: npm install --save-dev jest
    • Mocha + Chai: npm install --save-dev mocha chai

    Testing JavaScript with Jest & Mocha

    Master automated testing to catch bugs before production. Learn unit tests, mocking, async testing, API testing, and test-driven development with Jest and Mocha.

    What You'll Learn

    • Jest & Mocha test structure
    • Async testing with async/await
    • Mocking functions & modules
    • Fake timers
    • API testing with Supertest
    • Test coverage & TDD

    Why Automated Testing?

    Testing transforms "I think this works" into "I know this works." A solid test suite gives you confidence to refactor, fast feedback on bugs, and protection against regressions.

    Types of Tests

    • Unit tests โ€” Single function/module
    • Integration tests โ€” Multiple units together
    • E2E tests โ€” Full app through UI

    Jest vs Mocha

    • Jest โ€” All-in-one, zero-config
    • Mocha โ€” Minimal, highly configurable
    • Both excellent โ€” choice depends on ecosystem

    Jest Basics

    Jest is all-in-one: test runner, assertion library, mocking, coverage, and watch mode built in. Perfect for React and modern JavaScript projects.

    Jest Basics

    Basic test structure with describe, test, and expect

    Try it Yourself ยป
    JavaScript
    // Jest - Basic Test Structure
    
    // math.js - Code to test
    function sum(a, b) {
      return a + b;
    }
    
    function divide(a, b) {
      if (b === 0) {
        throw new Error("Cannot divide by zero");
      }
      return a / b;
    }
    
    module.exports = { sum, divide };
    
    // math.test.js - Jest tests
    const { sum, divide } = require("./math");
    
    // describe() groups related tests
    describe("sum()", () => {
      // test() or it() defines a single test case
      test("adds two positive numbers", () => {
        expect(sum(2, 3)).toBe(5);
     
    ...

    Mocha + Chai

    Mocha is a minimal test runner paired with Chai for assertions. This combination gives you fine-grained control over your testing stack.

    Mocha + Chai

    Flexible testing setup with Mocha and Chai assertions

    Try it Yourself ยป
    JavaScript
    // Mocha + Chai - Flexible Testing Setup
    
    // Install: npm install --save-dev mocha chai
    
    // math.js (same code)
    function sum(a, b) {
      return a + b;
    }
    
    function divide(a, b) {
      if (b === 0) {
        throw new Error("Cannot divide by zero");
      }
      return a / b;
    }
    
    module.exports = { sum, divide };
    
    // test/math.test.js - Mocha + Chai tests
    const { expect } = require("chai");
    const { sum, divide } = require("../math");
    
    describe("sum()", () => {
      it("adds two positive numbers", () => {
        const re
    ...

    Testing Async Code

    Real apps are full of async code: API calls, timers, database queries. Both Jest and Mocha handle async testing with async/await and Promises.

    Testing Async Code

    Handle async/await, Promises, and rejections in tests

    Try it Yourself ยป
    JavaScript
    // Testing Asynchronous Code
    
    // async.js - Async functions to test
    async function fetchUser(id) {
      const response = await fetch(`/api/users/${id}`);
      if (!response.ok) {
        throw new Error("User not found");
      }
      return response.json();
    }
    
    function delay(ms) {
      return new Promise(resolve => setTimeout(resolve, ms));
    }
    
    function delayedValue(value, ms) {
      return new Promise(resolve => {
        setTimeout(() => resolve(value), ms);
      });
    }
    
    module.exports = { fetchUser, delay, delayedValue };
    
    ...

    Mocking with Jest

    Mocking isolates the code under test by replacing dependencies with controlled substitutes. Jest includes powerful mocking capabilities built-in.

    Mocking with Jest

    Mock functions, modules, and fetch for isolated tests

    Try it Yourself ยป
    JavaScript
    // Mocking Functions, Modules & APIs
    
    // --- JEST MOCKING ---
    
    // 1. Mock functions
    const mockFn = jest.fn();
    
    mockFn("hello");
    mockFn("world");
    
    expect(mockFn).toHaveBeenCalled();
    expect(mockFn).toHaveBeenCalledTimes(2);
    expect(mockFn).toHaveBeenCalledWith("hello");
    
    // 2. Mock return values
    const mockGet = jest.fn()
      .mockReturnValue(10)              // Always returns 10
      .mockReturnValueOnce(5)           // Returns 5 first time
      .mockResolvedValue({ id: 1 })     // Returns Promise
      .mockR
    ...

    ๐Ÿ’ก When to Mock: Mock external services (APIs, databases), slow operations, unpredictable values (random, time), and modules with side effects. Keep tests fast, isolated, and deterministic.

    Sinon for Mocha

    Sinon provides spies, stubs, and mocks for Mocha. It's the companion library that gives Mocha the same mocking power Jest has built-in.

    Sinon for Mocha

    Spies, stubs, and mocks with Sinon library

    Try it Yourself ยป
    JavaScript
    // Sinon - Spies, Stubs & Mocks for Mocha
    
    // npm install --save-dev sinon
    
    const sinon = require("sinon");
    const { expect } = require("chai");
    
    // 1. Spies - Track function calls without changing behavior
    describe("Spies", () => {
      it("tracks calls", () => {
        const callback = sinon.spy();
        
        callback("hello");
        callback("world");
        
        expect(callback.calledTwice).to.be.true;
        expect(callback.calledWith("hello")).to.be.true;
        expect(callback.firstCall.args[0]).to.equal("he
    ...

    Testing Timers

    Test time-based logic like debounce, throttle, countdowns, and delays using fake timers. Control time programmatically for instant, reliable tests.

    Testing Timers

    Use fake timers for debounce, throttle, and countdowns

    Try it Yourself ยป
    JavaScript
    // Testing Timers & Time-Based Logic
    
    // timer.js - Code with timers
    function debounce(fn, delay) {
      let timeoutId;
      return function(...args) {
        clearTimeout(timeoutId);
        timeoutId = setTimeout(() => fn.apply(this, args), delay);
      };
    }
    
    function countDown(seconds, onTick, onComplete) {
      let remaining = seconds;
      const interval = setInterval(() => {
        remaining--;
        onTick(remaining);
        if (remaining === 0) {
          clearInterval(interval);
          onComplete();
        }
      }, 1000);
      
    ...

    API Testing with Supertest

    Test Express/Node.js APIs by making HTTP requests and asserting responses. Supertest makes API testing clean and expressive.

    API Testing with Supertest

    Test Express APIs with HTTP requests and assertions

    Try it Yourself ยป
    JavaScript
    // Testing Node.js APIs with Supertest
    
    // npm install --save-dev supertest
    
    // app.js - Express application
    const express = require("express");
    const app = express();
    
    app.use(express.json());
    
    const users = [
      { id: 1, name: "Alice" },
      { id: 2, name: "Bob" }
    ];
    
    app.get("/users", (req, res) => {
      res.json(users);
    });
    
    app.get("/users/:id", (req, res) => {
      const user = users.find(u => u.id === parseInt(req.params.id));
      if (!user) {
        return res.status(404).json({ error: "User not foun
    ...

    Test Coverage

    Coverage reports show which lines, branches, and functions your tests execute. Use coverage thresholds to enforce testing standards.

    Test Coverage

    Measure code quality with coverage reports and thresholds

    Try it Yourself ยป
    JavaScript
    // Test Coverage - Measuring Code Quality
    
    // Jest coverage: npm test -- --coverage
    // Or add to package.json:
    {
      "jest": {
        "collectCoverage": true,
        "coverageReporters": ["text", "html", "lcov"],
        "coverageDirectory": "coverage"
      }
    }
    
    // Coverage output example:
    // ---------------|---------|----------|---------|---------|
    // File           | % Stmts | % Branch | % Funcs | % Lines |
    // ---------------|---------|----------|---------|---------|
    // All files      |   85.71 |       80 | 
    ...

    Test-Driven Development (TDD)

    TDD means writing tests before implementation. This drives better design and ensures every feature has test coverage from the start.

    Test-Driven Development

    Write tests first, then implement to make them pass

    Try it Yourself ยป
    JavaScript
    // Test-Driven Development (TDD) Example
    
    // TDD Workflow:
    // 1. Write a FAILING test
    // 2. Write minimum code to PASS
    // 3. Refactor while keeping tests GREEN
    // 4. Repeat
    
    // Example: Build a password validator
    
    // Step 1: Write failing tests FIRST
    // passwordValidator.test.js
    const { validatePassword } = require("./passwordValidator");
    
    describe("validatePassword()", () => {
      test("returns valid for strong password", () => {
        expect(validatePassword("MyP@ssw0rd")).toEqual({
          valid: t
    ...

    Testing Best Practices

    Follow these patterns to write tests that are reliable, maintainable, and actually catch bugs instead of just passing.

    Testing Best Practices

    Write reliable, maintainable tests that catch bugs

    Try it Yourself ยป
    JavaScript
    // Testing Best Practices
    
    // 1. Test behavior, not implementation
    // โŒ BAD - Tests internal state
    test("sets internal flag", () => {
      const user = new User();
      user.activate();
      expect(user._isActive).toBe(true); // Private detail!
    });
    
    // โœ… GOOD - Tests observable behavior
    test("activated user can log in", () => {
      const user = new User();
      user.activate();
      expect(user.canLogIn()).toBe(true);
    });
    
    
    // 2. One assertion per test (mostly)
    // โŒ BAD - Multiple unrelated assertions
    test("user
    ...

    Key Takeaways

    Core Concepts

    • โœ“ describe/test/expect structure
    • โœ“ Async testing with async/await
    • โœ“ Mocking functions and modules
    • โœ“ Fake timers for time-based code
    • โœ“ API testing with Supertest

    Best Practices

    • โœ“ Test behavior, not implementation
    • โœ“ Use Arrange-Act-Assert pattern
    • โœ“ Test edge cases and errors
    • โœ“ Keep tests fast and isolated
    • โœ“ Aim for 80%+ coverage on critical code

    Sign up for free to track which lessons you've completed and get learning reminders.

    Previous