Matt Arbesfeld CEO @ LogRocket

JavaScript Testing: Chai (Part 1)

2 min read 592

At LogRocket, nearly our entire code base is JavaScript. Over the years, we have learned a good amount about the lesser-known features of JavaScript testing libraries. In this series, I’ll go through the LogRocket testing stack and describe some of the tips and tricks that help make our tests faster and more robust.

  • Part 1: Test Expectations — Chai
  • Part 2: Test Mocks — Sinon
  • Part 2: Test Frameworks — Jest and Mocha
  • Part 3: Integration Testing — WebdriverIO and Selenium


Chai is a “test expectation” library: it helps you make assertions about code behavior. Chai itself comes in many syntactic flavors that you can choose between for making assertions.

For example, if you wanted to assert that foo is a string, there are a few different options:

// should

// expect

// assert
chai.assert.typeOf(foo, ‘string’);


As JavaScript engineers, we’re really good at making sure our code works when the inputs are correct. The biggest challenge — and one of the best reasons to write tests — is to make assertions for failing or unexpected inputs.

Chai comes with a handy helper that lets us assert that code should throw an exception. This is great for throwing inputs like -1, ★★François★★ and function(){alert()} at code that expects a user’s name.

Here’s an example usage:

expect(() => {
}).to.throw(/an error/);

equal() vs eql()

Chai comes with a number of built-in functions for determining equality.equal() asserts that two arguments are referentially equal (ie. a === b). eql() does a deep equality check between two arguments.


expect('rocket').to.equal('rocket') // true
expect('rocket').to.eql('rocket') // true
expect({ log: 'rocket' }).to.equal({ log: 'rocket' }) // false
expect({ log: 'rocket' }).to.eql({ log: 'rocket' }) // true

Why use equal/eql at all instead of expect(foo === 'rocket') We get beautiful test output that looks like this:

Instead of this:

We made a custom demo for .
No really. Click here to check it out.


Chai can be extended with various plugins that provide additional functionality, such as chai-stringwhich adds handy string-testing functionality, chai-as-promisedwhich allows us to write assertions about Promises, and chai-datetimewhich provides date assertions.

One handy, lesser-known plugin is dirty-chai. Normally Chai assertions can only be made like so:


This can be brittle. If I accidentally make a typo, then the assertion will never be checked:


Instead, we use dirty-chai which extends chai with function calls like so:


This way, if I ever make a syntax mistake, the test will throw an exception instead of passing silently.


Sinon is an amazing library for writing test “spies” and “stubs” in JavaScript. With Sinon, we can mock functionality that doesn’t contribute to the test assertion. There are a number of reasons that you might want to use a stub or spy in a test:

  • Fake a network interface to see how a component responds to certain network conditions
  • Mock code that relies on interfacing with the DOM
  • Assert that a certain callback is invoked by a test.

sinon-chai allows us to make Sinon assertions with the Chai syntax:

const stub = sinon.spy();

I’ll describe Sinon more thoroughly in another blog post this series.


If you’re using eslint to lint your code, eslint-plugin-chai-expect prevents a few of the most common syntax errors:

expect(foo === bar); // no-inner-compare
expect(foo) // missing-assertion

Have any other lessons learned about assertions with Chai? Let me know in the comments!


: Debug JavaScript errors easier by understanding the context

Debugging code is always a tedious task. But the more you understand your errors the easier it is to fix them.

LogRocket allows you to understand these errors in new and unique ways. Our frontend monitoring solution tracks user engagement with your JavaScript frontends to give you the ability to find out exactly what the user did that led to an error.

LogRocket records console logs, page load times, stacktraces, slow network requests/responses with headers + bodies, browser metadata, and custom logs. Understanding the impact of your JavaScript code will never be easier!

Matt Arbesfeld CEO @ LogRocket

Leave a Reply