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

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:

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

dirty-chai

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:

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!

 

You come here a lot! We hope you enjoy the LogRocket blog. Could you fill out a survey about what you want us to write about?

    Which of these topics are you most interested in?
    ReactVueAngularNew frameworks
    Do you spend a lot of time reproducing errors in your apps?
    YesNo
    Which, if any, do you think would help you reproduce errors more effectively?
    A solution to see exactly what a user did to trigger an errorProactive monitoring which automatically surfaces issuesHaving a support team triage issues more efficiently
    Thanks! Interested to hear how LogRocket can improve your bug fixing processes? Leave your email:

    : 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