chai vs jasmine vs jest vs mocha vs sinon
Choosing the Right JavaScript Testing Stack
chaijasminejestmochasinonSimilar Packages:

Choosing the Right JavaScript Testing Stack

jest, mocha, jasmine, chai, and sinon form the core ecosystem for JavaScript testing, but they serve different roles. jest is an all-in-one framework handling runners, assertions, and mocks. mocha is a flexible test runner that pairs with chai for assertions and sinon for spies. jasmine is an older all-in-one solution often used in Angular projects. chai provides flexible assertion styles, while sinon specializes in standalone spies, stubs, and mocks. Understanding their distinct responsibilities helps teams build a maintainable testing strategy.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
chai08,266147 kB913 months agoMIT
jasmine038175.4 kB0a month agoMIT
jest045,3326.59 kB24322 days agoMIT
mocha022,8752.31 MB2325 months agoMIT
sinon09,7582.24 MB4615 days agoBSD-3-Clause

Choosing the Right JavaScript Testing Stack

Building reliable software requires a solid testing strategy. The JavaScript ecosystem offers several tools for this purpose, but they do not all do the same thing. jest, mocha, jasmine, chai, and sinon are the most common names you will encounter. Some are test runners, some check results, and others fake dependencies. Let's break down how they fit together and when to use each one.

πŸƒ Running Tests: Who Executes Your Code

The test runner is the engine that finds and executes your test files. It manages the lifecycle, reporting, and parallelization.

jest includes a built-in runner that works out of the box.

  • You write tests in files ending with .test.js.
  • It automatically finds and runs them without extra config.
// jest: Built-in runner
// math.test.js
const sum = require('./math');

test('adds 1 + 2 to equal 3', () => {
  expect(sum(1, 2)).toBe(3);
});

mocha is a dedicated runner that needs configuration to know where tests live.

  • You specify test files via command line or config.
  • It gives you control over timeouts and retries.
// mocha: Configured runner
// test/math.test.js
const sum = require('../math');

describe('Math', () => {
  it('adds 1 + 2 to equal 3', () => {
    // Needs assertion lib like Chai
    if (sum(1, 2) !== 3) throw new Error('Failed');
  });
});

jasmine also includes a runner but relies on a spec helper file.

  • You configure spec_dir in jasmine.json.
  • It runs tests sequentially by default.
// jasmine: Spec runner
// spec/math.spec.js
const sum = require('../src/math');

describe('Math', () => {
  it('adds 1 + 2 to equal 3', () => {
    expect(sum(1, 2)).toBe(3);
  });
});

chai and sinon do not run tests.

  • They are helper libraries used inside the runners above.
  • You cannot execute tests with only these two.
// chai/sinon: No runner
// These libraries provide assertions and spies
// They must be imported into a runner like Mocha or Jest
import { expect } from 'chai';
import sinon from 'sinon';

βœ… Checking Results: Assertion Styles

Assertions verify that your code behaves as expected. Different libraries offer different syntax styles.

jest uses a global expect function with chainers.

  • Syntax is readable and close to natural language.
  • No import needed in test files by default.
// jest: Global expect
const user = { name: 'Alice', active: true };

expect(user.name).toBe('Alice');
expect(user).toHaveProperty('active', true);
expect(user).toMatchObject({ name: 'Alice' });

chai offers three styles: expect, should, and assert.

  • expect is similar to Jest but requires import.
  • should modifies object prototypes (less common now).
// chai: Expect style
import { expect } from 'chai';

const user = { name: 'Alice', active: true };

expect(user.name).to.equal('Alice');
expect(user).to.have.property('active', true);
expect(user).to.include({ name: 'Alice' });

jasmine uses a global expect similar to Jest.

  • It was the original model for Jest's assertions.
  • Fewer matchers than Jest out of the box.
// jasmine: Global expect
const user = { name: 'Alice', active: true };

expect(user.name).toBe('Alice');
expect(user).toBeDefined();
expect(user.active).toBeTruthy();

mocha and sinon do not provide assertions.

  • mocha relies on chai or Node's built-in assert.
  • sinon focuses on behavior verification, not value checking.
// mocha/sinon: No assertions
// Mocha throws errors on failure, Sinon verifies calls
// You typically import Chai for value checks
import { expect } from 'chai';
expect(true).to.be.true;

🎭 Faking Dependencies: Mocks and Spies

Sometimes you need to isolate code by faking external calls like APIs or timers.

jest has built-in mocking functions.

  • jest.fn() creates a spy easily.
  • jest.mock() hoists module mocks to the top.
// jest: Built-in mocks
const mockFn = jest.fn();
mockFn('hello');

expect(mockFn).toHaveBeenCalled();
expect(mockFn).toHaveBeenCalledWith('hello');

jest.mock('axios');

sinon is the gold standard for standalone spies and stubs.

  • Works with any runner (Mocha, Jasmine, etc.).
  • Offers deep stubbing and fake timers.
// sinon: Standalone spies
import sinon from 'sinon';

const obj = { method: () => {} };
const spy = sinon.spy(obj, 'method');
obj.method();

sinon.assert.calledOnce(spy);
sinon.restore();

jasmine includes basic spying tools.

  • spyOn works on existing object methods.
  • Less powerful than Sinon for complex stubbing.
// jasmine: Built-in spies
const obj = { method: () => {} };
spyOn(obj, 'method').and.callThrough();
obj.method();

expect(obj.method).toHaveBeenCalled();

chai and mocha do not mock dependencies.

  • They rely on sinon or jest mocks for this.
  • chai can verify Sinon spies via plugins.
// chai/mocha: No mocking
// Use Sinon plugin for Chai to assert spy behavior
import chai from 'chai';
import sinonChai from 'sinon-chai';
chai.use(sinonChai);

expect(spy).to.have.been.called;

βš™οΈ Configuration and Setup

Setup time varies significantly between these tools.

jest aims for zero config.

  • Works immediately in most React projects.
  • Custom config goes in jest.config.js.
// jest: Minimal config
// jest.config.js
module.exports = {
  testEnvironment: 'jsdom',
  coverageDirectory: 'coverage'
};

mocha requires manual wiring.

  • You must install runners, assertions, and mocks separately.
  • Configured via .mocharc.js or command line.
// mocha: Manual config
// .mocharc.js
module.exports = {
  extension: ['js'],
  spec: 'test/**/*.test.js',
  require: ['@babel/register']
};

jasmine needs a spec helper.

  • Init with jasmine init.
  • Config lives in spec/support/jasmine.json.
// jasmine: Spec config
// spec/support/jasmine.json
{
  "spec_dir": "spec",
  "spec_files": ["**/*[sS]pec.js"]
}

chai and sinon are imports only.

  • No config files needed for the libs themselves.
  • Config depends on the runner you pair them with.
// chai/sinon: Import only
// No config file, just install and import
import chai from 'chai';
import sinon from 'sinon';

🀝 Similarities: Shared Ground

Despite their differences, these tools share common goals and patterns.

1. πŸ§ͺ Async Testing Support

  • All runners handle async/await and Promises.
  • You can test asynchronous code without callbacks.
// jest
test('fetches data', async () => {
  const data = await fetchData();
  expect(data).toBeDefined();
});

// mocha
it('fetches data', async () => {
  const data = await fetchData();
  // assert with Chai
});

// jasmine
it('fetches data', async () => {
  const data = await fetchData();
  expect(data).toBeDefined();
});

2. πŸ” Hook Support

  • Setup and teardown hooks are available in all runners.
  • Use beforeEach, afterEach for clean state.
// jest
beforeEach(() => { /* setup */ });
afterEach(() => { /* cleanup */ });

// mocha
beforeEach(function() { /* setup */ });
afterEach(function() { /* cleanup */ });

// jasmine
beforeEach(() => { /* setup */ });
afterEach(() => { /* cleanup */ });

3. πŸ“¦ Ecosystem Integration

  • All work with TypeScript via type definitions.
  • CI/CD pipelines support all of them equally.
// All: TypeScript support
// tsconfig.json includes test files
// jest types: @types/jest
// mocha types: @types/mocha
// chai types: @types/chai

πŸ“Š Summary: Capabilities Matrix

Featurejestmochajasminechaisinon
Test Runnerβœ… Built-inβœ… Built-inβœ… Built-in❌❌
Assertionsβœ… Built-in❌ (Needs Chai)βœ… Built-inβœ… Standalone❌
Mockingβœ… Built-in❌ (Needs Sinon)βœ… BasicβŒβœ… Standalone
Config Effort🟒 Low🟠 High🟠 Medium🟒 None🟒 None
Best ForReact/GeneralCustom NodeLegacy/AngularCustom AssertionsComplex Mocks

πŸ†š Summary: Stack Combinations

StackComponentsUse Case
Modern Defaultjest onlyReact, Vue, general JS apps
Classic Flexiblemocha + chai + sinonComplex Node.js, custom needs
Legacy Angularjasmine + karmaOlder Angular projects
Assertion Focusmocha + chaiWhen you only need custom assertions
Mocking Focusjest + sinonWhen Jest mocks aren't enough

πŸ’‘ The Big Picture

jest is the default choice for most teams today. It removes the friction of stitching tools together. If you are starting a new React or general JavaScript project, this is usually the right pick.

mocha + chai + sinon is the power user stack. It requires more setup but gives you full control over every layer. Choose this if you have specific requirements that Jest cannot meet.

jasmine is stable but aging. It still powers many Angular projects, but new projects rarely choose it over Jest.

Final Thought: Don't overcomplicate your setup. Start with jest unless you have a clear reason to build a custom stack. Testing should support your development β€” not slow it down.

How to Choose: chai vs jasmine vs jest vs mocha vs sinon

  • chai:

    Choose chai if you need flexible assertion styles like expect, should, or assert within a custom test runner setup. It is ideal when paired with mocha for projects requiring specific assertion syntax that other frameworks do not support. Use it when you want to mix and match testing tools rather than adopting a full suite.

  • jasmine:

    Choose jasmine if you are maintaining legacy Angular projects or prefer a batteries-included framework without external dependencies. It works well for teams that want a stable, opinionated structure without the complexity of configuring multiple libraries. Avoid it for new React projects where jest offers better integration and community support.

  • jest:

    Choose jest for most modern JavaScript projects, especially React applications, due to its zero-config setup and all-in-one capabilities. It handles test running, assertions, and mocking out of the box, reducing the need for multiple dependencies. It is the best choice for teams prioritizing developer speed and strong ecosystem integration.

  • mocha:

    Choose mocha if you need maximum flexibility to configure your test runner and select your own assertion and mocking libraries. It is suitable for complex Node.js environments where custom reporting or specific async control is required. Use it when jest feels too opinionated or restrictive for your architecture.

  • sinon:

    Choose sinon when you need powerful standalone spies, stubs, and mocks that work across any test runner. It is essential for testing legacy code or complex dependencies where built-in mocking tools fall short. Pair it with mocha and chai for a classic, highly customizable testing stack.

README for chai

ChaiJS
chai

Chai is a BDD / TDD assertion library for node and the browser that can be delightfully paired with any javascript testing framework.

downloads:? node:?
Join the Slack chat Join the Gitter chat OpenCollective Backers

For more information or to download plugins, view the documentation.

What is Chai?

Chai is an assertion library, similar to Node's built-in assert. It makes testing much easier by giving you lots of assertions you can run against your code.

Installation

Node.js

chai is available on npm. To install it, type:

$ npm install --save-dev chai

Browsers

You can also use it within the browser; install via npm and use the index.js file found within the download. For example:

<script src="./node_modules/chai/index.js" type="module"></script>

Usage

Import the library in your code, and then pick one of the styles you'd like to use - either assert, expect or should:

import { assert } from 'chai';  // Using Assert style
import { expect } from 'chai';  // Using Expect style
import { should } from 'chai';  // Using Should style

Register the chai testing style globally

import 'chai/register-assert';  // Using Assert style
import 'chai/register-expect';  // Using Expect style
import 'chai/register-should';  // Using Should style

Import assertion styles as local variables

import { assert } from 'chai';  // Using Assert style
import { expect } from 'chai';  // Using Expect style
import { should } from 'chai';  // Using Should style
should();  // Modifies `Object.prototype`

import { expect, use } from 'chai';  // Creates local variables `expect` and `use`; useful for plugin use

Usage with Mocha

mocha spec.js --require chai/register-assert.js  # Using Assert style
mocha spec.js --require chai/register-expect.js  # Using Expect style
mocha spec.js --require chai/register-should.js  # Using Should style

Read more about these styles in our docs.

Plugins

Chai offers a robust Plugin architecture for extending Chai's assertions and interfaces.

  • Need a plugin? View the official plugin list.
  • Want to build a plugin? Read the plugin api documentation.
  • Have a plugin and want it listed? Simply add the following keywords to your package.json:
    • chai-plugin
    • browser if your plugin works in the browser as well as Node.js
    • browser-only if your plugin does not work with Node.js

Related Projects

Contributing

Thank you very much for considering to contribute!

Please make sure you follow our Code Of Conduct and we also strongly recommend reading our Contributing Guide.

Here are a few issues other contributors frequently ran into when opening pull requests:

  • Please do not commit changes to the chai.js build. We do it once per release.
  • Before pushing your commits, please make sure you rebase them.

Contributors

Please see the full Contributors Graph for our list of contributors.

Core Contributors

Feel free to reach out to any of the core contributors with your questions or concerns. We will do our best to respond in a timely manner.

Keith Cirkel James Garbutt KristjΓ‘n Oddsson

Core Contributor Alumni

This project would not be what it is without the contributions from our prior core contributors, for whom we are forever grateful:

Jake Luer Veselin Todorov Lucas Fernandes da Costa Grant Snodgrass