mrkeyoor.com_
Sun 20 Sept 02:42 UTC
npmTestingupdated 19 Sept 2026

chai review

Chai 6.2.2 is a test-runner-neutral assertion library for Node and browsers. It supplies expect, assert, and should interfaces over the same assertion engine, including deep equality, property paths, collection membership, numeric ranges, and synchronous exception checks. Chai does not discover tests, create spies, fake timers, or await rejected promises by itself. Those jobs belong to a runner or plugin. The current patch removes a BigInt literal from closeTo so older parsers can load that assertion implementation. Our browser build measured 63.9 KB minified and 16.6 KB gzipped, which is sizeable for assertions that should usually stay outside production code.

107.6Mdownloads / wk
Verdict

Chai 6.2.2 installed in 0.4 seconds as one 1 MB package with no audit findings in our sandbox, but its full browser import reached 16.6 KB gzipped. Use it when a runner-neutral assertion language or a Chai plugin is a real requirement; otherwise node:assert/strict or the matchers already built into your test runner are simpler.

We installed it

Lab card: what happened when we installed chaiScreenshot of chai documentation
Install✓ · 0.4s1 package on disk · 1 MB
ImportESM import works · require() works · ESM package
Browser16.6 KBgzipped (63.9 KB minified), bundled with esbuild
Typesno TypeScript types found
Known vulns00 critical · 0 high · 0 moderate · 0 low (npm audit)

Answers from our run

Does chai install cleanly?

Yes. In a fresh container with an empty cache, npm install chai finished in 0.4s, leaving 1 package and 1 MB on disk. npm audit reported no known vulnerabilities.

How much does chai add to a browser bundle?

16.6 KB gzipped (63.9 KB minified) when the whole package is bundled for the browser with esbuild. Importing only part of it is usually smaller.

Does chai work with both ESM and CommonJS?

Yes. Both import 'chai' and require('chai') worked in Node 22 in our run. The package is published as ESM.

Does chai include TypeScript types?

No type declarations were found in our install, so TypeScript users need their own declarations.

chai or should: which should you use?

should: Choose it for should-style assertions when modifying Object.prototype is an accepted project convention. Chai 6.2.2 installed in 0.4 seconds as one 1 MB package with no audit findings in our sandbox, but its full browser import reached 16.6 KB gzipped.

When should you not use chai?

Node's built-in node:assert/strict already covers the suite; Chai would add a 168 KB package and another assertion vocabulary.

API stability4/5Chai 6 keeps the long-standing expect, assert, should, use, and config entry points, and the 6.2.2 patch changes closeTo internals without changing its call shape. The fluent language is mature, but modifiers such as deep, own, nested, ordered, any, and all alter later assertions in ways that deserve direct tests. Major releases also changed the Node floor and ESM packaging, so a major upgrade still needs runner and plugin checks.
Docs4/5The official documentation separates the three interfaces, lists assertion chains, explains plugin registration, and includes guides for configuration and plugin authors. Its examples make strict versus deep equality and thrown-error usage clear. Promise assertions, spies, and runner integration often lead into separate plugin or runner documentation, so readers must verify compatibility outside the core site before copying an older recipe.
Maintenance4/5The repository is unarchived, was pushed on August 24, 2026, and GitHub reported 91 open issues and pull requests. Release 6.2.2 shipped in December 2025 with a runtime-compatibility fix for closeTo plus build updates. Continued repository activity is visible, although the latest package is several months behind the latest push and much of the recent release work is maintenance rather than new assertion behavior.
Ecosystem5/5npm recorded 112,487,076 downloads from August 19 through 25, 2026, while GitHub showed 8,269 stars. Chai works independently of a test runner and has plugins for promise, spy, and domain-specific assertions. That reach is useful in Mocha-era suites, but current Jest and Vitest projects already ship matcher systems, and every third-party Chai plugin adds its own major-version compatibility question.

Use it if

  • A Mocha or custom test setup needs readable assertions without changing its runner or lifecycle hooks.
  • The suite mixes fluent expect calls with function-style assert calls and should share one failure model.
  • Tests need deep property, subset, member, exception, or tolerance assertions beyond Node's basic examples.
  • An established Chai plugin supplies a specific matcher that the team already relies on.
Skip it if

Setup reality

We installed Chai 6.2.2 in a fresh Node 22 Bookworm sandbox. npm finished in 0.4 seconds and left one package using 1 MB on disk. Chai itself was 168 KB unpacked, declared no direct or peer dependencies, and npm audit reported 0 known vulnerabilities. Its engine floor is Node 18. The package is ESM without an exports map; both require() and ESM import worked in our checks. We found no TypeScript declaration files.

Install it as a development dependency and import one interface explicitly. expect and assert do not modify globals. Calling should() adds a should getter to Object.prototype, which can surprise code that enumerates inherited properties. The register entry points can create globals for a runner preload, but local imports make test ownership easier to see. No credentials or configuration file are required.

Chai only evaluates the value presented to an assertion. For a synchronous exception, pass a function instead of calling it first. For a promise, await the promise and assert its result with core Chai, or add a promise plugin and await that plugin's assertion. Plugin registration mutates the imported Chai instance, so run setup before test modules and check that each plugin supports Chai 6.

The browser build from our full namespace import was 63.9 KB minified and 16.6 KB gzipped. Tree-shaking may differ with narrower imports, but test code should be excluded from production bundles. Version 6.2.2 changes closeTo internals to avoid a BigInt literal parsing problem; its public tolerance remains an absolute delta. Chained words such as to and be are language helpers, while deep, own, nested, ordered, any, and all change assertion behavior.

Patterns

Check primitive results check-scalars

import { expect } from 'chai';

expect(status).to.equal('ready');
expect(count).to.be.greaterThan(0);
expect(cached).to.equal(true);

equal uses strict equality. The words to and be only make the chain read naturally.

Compare nested values compare-deep-values

expect(actual).to.deep.equal({
  id: 7,
  labels: ['paid', 'priority'],
  owner: { active: true },
});

deep changes object and array comparison from reference identity to recursive value comparison.

Use the assert interface use-assert-functions

import { assert } from 'chai';

assert.strictEqual(response.status, 200);
assert.deepEqual(response.body, { ok: true });
assert.match(response.type, /^application\/json/);

assert exposes function calls from the same library, which suits suites that avoid fluent chains.

Check a synchronous error assert-sync-throw

expect(() => decodeToken('bad')).to.throw(TypeError, /token/);
expect(() => decodeToken(validToken)).not.to.throw();

Give expect a function. If decodeToken runs before expect, Chai cannot inspect the thrown error.

Read a property path inspect-nested-property

expect(payload).to.have.nested.property('users[0].role', 'admin');
expect(payload).to.have.own.property('requestId');

nested interprets dots and brackets as a path. own restricts the check to properties on the object itself.

Compare members without order match-unordered-members

expect(['write', 'read']).to.have.members(['read', 'write']);
expect(rows).to.have.deep.members([{ id: 1 }, { id: 2 }]);

members ignores order. Add ordered when position is part of the contract.

Match selected fields check-object-subset

expect(order).to.deep.include({
  state: 'paid',
  currency: 'USD',
});
expect(order).to.include.all.keys('id', 'createdAt');

A subset check leaves unrelated response fields free to change without weakening the fields named here.

Allow floating-point error compare-with-tolerance

expect(0.1 + 0.2).to.be.closeTo(0.3, 1e-12);
expect(progress).to.be.within(0, 1);

closeTo uses an absolute delta. Chai 6.2.2 changed its internal BigInt handling without changing this API.

Check strings and arrays check-containment

expect(message).to.include('saved');
expect(scopes).to.include('reports:read');
expect(scopes).to.include.members(['profile:read']);

include on an array checks membership. include.members permits values beyond the expected subset.

Register one plugin register-plugin

import * as chai from 'chai';
import sinonChai from 'sinon-chai';

chai.use(sinonChai);
chai.expect(save).to.have.been.calledOnceWith(42);

use changes this Chai instance. Load setup before test files and verify that the plugin supports Chai 6.

Assert an awaited value test-promise-result

const user = await loadUser(42);

expect(user).to.deep.include({ id: 42, active: true });

Core Chai does not await application work. Resolve the promise before making a normal assertion.

Limit assertion output configure-diff-output

import { config, expect } from 'chai';

config.truncateThreshold = 120;
expect(actualPayload).to.deep.equal(expectedPayload);

truncateThreshold controls displayed value length in failures and applies to the imported Chai instance.

Alternatives

PackageRegistryPick it when
shouldnpmChoose it for should-style assertions when modifying Object.prototype is an accepted project convention.
expectnpmChoose it for a smaller standalone expect API outside Jest or Vitest.
sinonnpmChoose it when spies, stubs, mocks, and fake timers are the actual missing test tools.

More testing guides

pytest · vitest · jsdom · playwright · coverage · axe-core · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.