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

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 chai.should(); foo.should.be.a(‘string’); // expect chai.expect(foo).to.be.a(‘string’); // assert chai.assert.typeOf(foo, ‘string’);
expect().to.throw()
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(() => { callSomeFunction(); }).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.
Example:
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').to.be.true
? We get beautiful test output that looks like this:

Instead of this:

dirty-chai
Chai can be extended with various plugins that provide additional functionality, such as chai-string
which adds handy string-testing functionality, chai-as-promised
which allows us to write assertions about Promises, and chai-datetime
which provides date assertions.
One handy, lesser-known plugin is dirty-chai
. Normally Chai assertions can only be made like so:
expect(foo).to.be.true
This can be brittle. If I accidentally make a typo, then the assertion will never be checked:
expect(foo).to.be.frue
Instead, we use dirty-chai
which extends chai with function calls like so:
expect(foo).to.be.true()
This way, if I ever make a syntax mistake, the test will throw an exception instead of passing silently.
sinon-chai
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(); expect(stub).to.be.calledOnce();
I’ll describe Sinon more thoroughly in another blog post this series.
eslint-plugin-chai-expect
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).to.be.true(); // no-inner-compare expect(foo) // missing-assertion
Have any other lessons learned about assertions with Chai? Let me know in the comments!

Plug: LogRocket, a DVR for web apps

LogRocket is a frontend logging tool that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.
In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single page apps.