Chai Assertions

Three Assertion Styles

const chai = require('chai'); // 1. assert style (TDD) const { assert } = chai; assert.equal(3, 3); assert.strictEqual('hello', 'hello'); assert.deepEqual({ a: 1 }, { a: 1 }); assert.isNull(null); assert.isArray([]); assert.include([1, 2, 3], 2); assert.throws(() => { throw new Error('oops') }, Error, 'oops'); // 2. expect style (BDD) — most popular const { expect } = chai; expect(42).to.equal(42); expect('hello').to.be.a('string'); expect([1, 2, 3]).to.have.lengthOf(3); expect(null).to.be.null; expect(undefined).to.be.undefined; expect(true).to.be.true; // 3. should style (BDD, extends Object.prototype) chai.should(); (42).should.equal(42); 'hello'.should.be.a('string'); [1, 2, 3].should.have.lengthOf(3); // Note: doesn't work on null/undefined values directly

expect — Chaining & Common Assertions

const { expect } = require('chai'); // Type checks expect(42).to.be.a('number'); expect('hello').to.be.a('string'); expect([]).to.be.an('array'); expect({}).to.be.an('object'); expect(() => {}).to.be.a('function'); // Equality expect('foo').to.equal('foo'); // strict === expect([1, 2]).to.deep.equal([1, 2]); // deep equality expect({ a: 1 }).to.eql({ a: 1 }); // alias for deep.equal // Object properties expect({ a: 1, b: 2 }).to.have.property('a', 1); expect({ a: 1, b: 2 }).to.include({ a: 1 }); expect(obj).to.have.all.keys('a', 'b'); expect(obj).to.have.any.keys('a', 'c'); // Arrays / strings expect([1, 2, 3]).to.include(2); expect('hello world').to.include('world'); expect([1, 2, 3]).to.have.members([3, 2, 1]); // order-independent expect([1, 2, 3]).to.have.ordered.members([1, 2, 3]); // Ranges expect(7).to.be.above(5); expect(3).to.be.below(5); expect(5).to.be.within(1, 10); // Regex expect('hello123').to.match(/\d+/); // Negation expect('foo').to.not.equal('bar'); expect([]).to.not.include(1);

Deep Equality & Nested Objects

// Deep equal — nested objects and arrays expect({ a: { b: { c: 3 } } }).to.deep.equal({ a: { b: { c: 3 } } }); // Nested property access expect({ a: { b: 1 } }).to.have.nested.property('a.b', 1); expect({ arr: [1, 2, 3] }).to.have.nested.property('arr[1]', 2); // Deep include — partial match expect({ a: 1, b: 2, c: 3 }).to.deep.include({ a: 1, b: 2 }); expect([{ a: 1 }, { b: 2 }]).to.deep.include({ a: 1 }); // Own properties only expect({ a: 1 }).to.have.own.property('a'); // Chai-subset plugin for partial matching const chaiSubset = require('chai-subset'); chai.use(chaiSubset); expect({ a: 1, b: 2, c: 3 }).to.containSubset({ a: 1, b: 2 });

Exception Assertions

// Synchronous throws expect(() => JSON.parse('invalid')).to.throw(); expect(() => JSON.parse('invalid')).to.throw(SyntaxError); expect(() => throw new Error('boom')).to.throw('boom'); expect(() => throw new Error('Not found')).to.throw(/not found/i); // Not throw expect(() => JSON.parse('{"valid": true}')).to.not.throw(); // Async throws — with chai-as-promised const chaiAsPromised = require('chai-as-promised'); chai.use(chaiAsPromised); await expect(Promise.reject(new Error('async error'))) .to.be.rejectedWith('async error'); await expect(asyncFunction()) .to.eventually.have.property('id'); await expect(asyncFunction()) .to.be.fulfilled;

Plugins

PluginInstallPurpose
chai-as-promisednpm i chai-as-promisedAsync/Promise assertions
sinon-chainpm i sinon-chaiSinon spy/stub assertions
chai-httpnpm i chai-httpHTTP request testing
chai-subsetnpm i chai-subsetPartial object matching
chai-datetimenpm i chai-datetimeDate comparison
// sinon-chai usage const sinon = require('sinon'); const sinonChai = require('sinon-chai'); chai.use(sinonChai); const spy = sinon.spy(); spy('arg1'); expect(spy).to.have.been.calledWith('arg1'); expect(spy).to.have.been.calledOnce;

Custom Messages & Custom Assertions

// Custom failure message (last argument to most assertions) expect(user.age, 'user age should be a number').to.be.a('number'); assert.isAbove(response.time, 0, 'response time must be positive'); // Add custom assertion method chai.Assertion.addMethod('validEmail', function () { const email = this._obj; new chai.Assertion(email).to.be.a('string'); this.assert( /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email), `expected #{this} to be a valid email`, `expected #{this} not to be a valid email` ); }); // Usage: expect('user@example.com').to.be.a.validEmail(); expect('invalid-email').to.not.be.a.validEmail();