Unit testing Javascript

Framework logos

Unit testing is like flossing. Everyone claims to do it, you know you should do it, and it’s devilishily difficult to make part of your routine. Yet, like good dental hygiene, there’s a definite pay back.

As I developed the Expression Language Emulator it become clear that I would need a decent set of unit tests to validate all the functions I had implemented. I hadn’t done unit testing of Javascript before, but a quick search led me to Chai for assertions and Mocha for unit testing.

What I didn’t find was many examples of how to test in the browser: it seems all the kewl kids are unit testing in Node. So, a quick post on how to unit test in the browser:

The first step is to download the Mocha and Chai libraries from their respective repos. You’ll need mocha.js, mocha.css and chai.js.

Place these in a directory along with an HTML page to run the tests. For the emulator, my page ended up like this (luxon is a date/time library - required for my tests but not by the frameworks).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<!DOCTYPE html>
<html>
  <head>
    <title>Mocha Tests</title>
    <meta charset="utf-8">
    <link rel="stylesheet" href="mocha.css">

  </head>
  <body>
    <div id="mocha"></div>
    <script src="mocha.js"></script>
  	<script src="chai.js"></script>
  	<script src="../luxon.js"></script>
    <script>
    var expect = chai.expect;
    
    mocha.setup('bdd')
    </script>
    <script src="../elshim.js"></script>
    <script src='strings.js'></script>
    <script src='collections.js'></script>
    <script src='logical.js'></script>
    <script src='conversion.js'></script>
    <script src='math.js'></script>
    <script src='date.js'></script>
    
    <script>
    	mocha.run();
    </script>
  </body>
</html>

I split the tests into a JS file per aspect of the language. For example, a set of tests for one date function looks like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
describe('Date functions',function(){
	describe('#addDays()', function () {
		context('with arguments', function(){
			it('should throw an exception for wrong argument types', function(){
				expect(function() {
					addDays("2",true,4)}).to.throw(TypeError);
			});
			it('should throw an exception for null argument types', function(){
				expect(function() {
					addDays(null,true,4)}).to.throw(TypeError);
			});
			it('should match MSFT example #1', function(){
				expect(addDays('2018-03-15T13:00:00Z', 10)).to.be.equals("2018-03-25T13:00:00.0000000Z");
			});
			it('should match MSFT example #2', function(){
				expect(addDays('2018-03-15T00:00:00Z', -5)).to.be.equals("2018-03-10T00:00:00.0000000Z");
			});
			it('should return 10 days ahead for passed timestamp in given format', function(){
				expect(addDays('2018-03-15T13:00:00Z', 10,'R')).to.be.equals("Sun, 25 Mar 2018 13:00:00 GMT");
			});
		});
		context('without arguments', function() {
			it('should throw an exception', function() {
				expect(function() {
					addDays()}).to.throw(TypeError);
			});
		});
	});
    ...

Nest the describe and context calls as appropriate to structure your tests. Use expect to execute your test and function chain to test results.

That’s pretty much it. It’s a lot of typing, but 578 tests and many discovered bugs later I have reasonable confidence that the emulator works and found errors in the Microsoft documentation that they were able to resolve.