Lessons Learnt from Unit Testing with Mocha, Chai and Sinon

Lessons Learnt from Unit Testing with Mocha, Chai and Sinon

In this post, I share my top lessons learnt writing unit tests with Mocha, Chai and Sinon.

Mocha is a test runner, Chai is an Expectation library and Sinon helps with test doubles such as stubs and mocks.

A more popular alternative nowadays is Jest, which is easier and simpler to setup and start using; but for some reason, it does not work with CoffeeScript (which is deprecated).

Setting up Mocha, Chai and Sinon requires some research and experimentation.

Photo by Shahadat Rahman / Unsplash

Expect

I've grown the habit of using expect instead of should or assert, from my technical exposure of the RSpec Library on Ruby on Rails.

"expect(result).to.equal(value)" is simple to read.

Promise

You can use Promises in your tests without having to use await or callbacks by using the chaiAsPromised library.

expect(result).to.eventually.have.property('status')

Result returns a promise.

Mock Libraries

You can use test doubles (stubs, mocks, fakes) efficiently with sinon. A test double is basically a false value that temporarily replaces the real value. For instance:

sandbox = sinon.createSandbox() sandbox.stub(bcrypt, 'compareSync').returns(true)

Whenever the function bcrypt.compareSync is called, whichever parameter is used, the return value is always true.

After the test done, you can restore the original functionality by using sandbox.restore().

Test File Name

The structure I use for the test files is creating a folder named tests/unit or tests/integration or tests/e2e depending on the type of tests. I work mostly with unit tests as a developer, but do sometimes experiment with integration ones using a testing environment and database (especially with Ruby On Rails) .

Test Structure

imports

describe theFileName
	before All Test
	after All Test

	describe each function
		beforeEach
		afterEach

		it should expect something
			expect value to be another value

If the codes use await async, it's easier to have more code coverage - but what happens if callbacks (as well) are used?

it '', (done) ->
	// codes here
	done()

You can use a done parameter and call it when the expectation is completed.

Efficiency

Made with Canon 5d Mark III and loved analog lens, Leica APO Macro Elmarit-R 2.8 / 100mm (Year: 1993)
Photo by Markus Spiske / Unsplash

As a matter of productivity and efficiency, I target around at most 80% of code coverage for files. This covers most of the function flow and logic. The rest is usually about simple if else checks such as checking if a value is present, otherwise throwing an error.