Stop 12 · TestingTopics · 4/9

Mocha & Chai

Part of the Backend Developer Roadmap

In short

Plenty of Node codebases don't use Jest. They use Mocha to run tests and Chai to check results. You'll meet this pair in real jobs, so it helps to read it comfortably.

A flexible, unopinionated test runner (Mocha) commonly paired with an assertion library (Chai) — an older but still widely used alternative to Jest in many Node.js codebases.

The blob character holds a stopwatch and whistle while a judge sips tea and signals.

Think of it like this

The contest host and the judge

At a college cooking contest, the host runs the show: starts each round, keeps time, announces results. The judge just tastes and says pass or fail. Mocha is the host, organising and running the tests. Chai is the judge, making an assertion about each result.

Watch it happen

Two ways to set up a Node test suite

Jest

  • Runner, assertions and mocks in one
  • Little setup to get started
  • A common default in many projects

Mocha + Chai

  • Mocha runs tests; Chai asserts
  • Pick your own mocks, like Sinon
  • Common in older Node codebases

Both test the same things well. Use whatever the project already uses; for a fresh project, Jest, Vitest or node:test need the least setup.

The big ideas

What you need to know

Mocha runs, that's all

Mocha gives you describe, it, hooks and reporters. It doesn't mind which assertion library you use.

Chai asserts

Chai offers expect, should and assert styles. expect(total).to.equal(60) is the most common.

Mix and match

Add Sinon for spies and stubs, and c8 for coverage. More setup, but more control.

Config in .mocharc

A .mocharc.json file sets the spec pattern, timeout and any loader, such as one for TypeScript.

See it in code

Walk through the code

ts
1import { expect } from "chai";2import { Cart } from "../src/cart.js";3 4describe("Cart", function () {5  let cart: Cart;6  beforeEach(function () { cart = new Cart(); });7 8  it("totals items", function () {9    cart.add({ name: "samosa", price: 20 }, 3);10    expect(cart.total()).to.equal(60);11  });12 13  it("rejects negative quantities", function () {14    expect(() => cart.add({ name: "chai", price: 15 }, -1)).to.throw("Invalid quantity");15  });16});

1/4Chai provides expect. Mocha supplies the describe and it functions.

Run it with npx mocha. describe, it and beforeEach are globals that Mocha provides while running.

Save yourself the pain

Common mistakes

Oops

Using arrow functions everywhere, then calling this.timeout().

Do this instead

Arrow functions don't get Mocha's this. Use function () {} when you need this.timeout or this.retries.

Oops

Passing a promise to expect without awaiting it.

Do this instead

Await the result first, or use chai-as-promised with await expect(p).to.be.rejected, or you're checking a pending promise.

Your mission

Read and run a Mocha suite

~25 min

Quick check

In a Mocha and Chai suite, which library decides whether expect(total).to.equal(60) passes?

Go deeper

  1. 1Set up Mocha + Chai and write a test equivalent to one you wrote in Jest.
  2. 2Compare Chai's `expect`/`should` assertion styles to Jest's built-in `expect`.
  3. 3Understand why some existing codebases still use Mocha+Chai over Jest.

More topics at this stop

Stuck on this topic? Ask an Insider

Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.

Download
InsideEdge