Mocha, Part 2
This post refers to work on the 7th of August which was day 54 of my #100daysofcode
So this morning I opened up another project which is gloriously test free and set about adding some test cases to it. This project has a lot of ‘modules’ which are fairly easily instantiated and used without any dependencies on being run in the browser so they are far better candidates for unit testing. I realise that the code I was trying to test yesterday was more integration testing than unit testing.
First things first, I put modules in quote marks because whilst the files create objects they are not any form of correctly defined modules at the minute. They are also written in a very old-fashioned code style (I wrote them 5 years ago).
The setup
I have two styles of objects in this project, those that are instantiated only once are declared as object literals and those which may be instantiated more than once and are declared as functions with the methods attached to the prototype. I did it this was because as I understand it in javascript an object literal that is instantiated more than once is not memory efficient: All of the inner functions are created again and again when you stamp out a copy, and as the inner functions are not attached to a prototype you cannot modify all of those functions at once by changing a prototype, you would have to track this yourself. I am not sure I want or will need to do the latter but I certainly want to be memory efficient if possible.
In order to use them with Mocha I feel I am going to have make them actual modules. So I start with the static objects which are going to be straightforward:
var Armors = {armors: [
{"armorDamageModifier": 1, "armorName": "Plain Clothes"},
{"armorDamageModifier": 1.1, "armorName": "Cheap Leather"}, {"armorDamageModifier": 1.2, "armorName": "Sturdy Leather"}, {"armorDamageModifier": 1.3, "armorName": "Hardened Leather"}, {"armorDamageModifier": 1.4, "armorName": "Rigid Linen"}]
,getArmorDamageModifier: function(armorID){return this.armors[armorID].armorDamageModifier;
}
,getArmorName: function(armorID){return this.armors[armorID].armorName;
}
};
To update this to a module which can be loaded in the NodeJS based test scripts all I need to add is:
if (typeof module!='undefined' && module.exports){ module.exports = function(){return Armors;
};
}
Except that I have copied this pattern from a module which expects the module to be instantiated with a new keyword (at least that is how I understand what is happening, in my test I must require the file and create a new copy of it before it works).
I dig around a bit and realise there is a far simpler pattern to use:
if (typeof module!='undefined' && module.exports){module.exports = Armors;
}
I am adding the optional ability to make the file a module because at the minute the whole thing is used in a HTML file in the old-fashioned global object way and I do not yet want to go through the whole process of fixing the HTML file, I just want to unit test the modules.
Alongside Mocha I am using shouldjs, because I used it yesterday and it seems to have everything I need. So I require my dependencies and check that I have a correctly instantiated Armors object by logging it to the console:
var Armor = require('../Armor');var should = require('should');console.log(Armor);
Which outputs:
{ armors:
[ { armorDamageModifier: 1, armorName: ‘Plain Clothes’ },
{ armorDamageModifier: 1.1, armorName: ‘Cheap Leather’ },
{ armorDamageModifier: 1.2, armorName: ‘Sturdy Leather’ },
{ armorDamageModifier: 1.3, armorName: ‘Hardened Leather’ },
{ armorDamageModifier: 1.4, armorName: ‘Rigid Linen’ } ],
getArmorDamageModifier: [Function],
getArmorName: [Function] }Great stuff, I have something to test at last.
The tests
I am not a DevOps person, but I have worked in companies with DevOPs departments and people. And also I have worked with some good developers who test their code. I am not 100% certain exactly what I should be testing at this point, but I do know that if I am testing something it has to be better than testing nothing.
Looking at my ‘Armor’ object I think that I should test the following to start with making sure things exist:
- It has five objects in the armors array
- It has a function called getArmorDamageModifier
- it has a function called getArmorName
The basic outer shell is going to be pretty simple, I do not need any set up or tear down steps so following the Mocha docs I have:
describe('Armor', function(){...
});
I am uncertain exactly the best way to structure the ‘describe()’ statements as the Mocha docs tend to have a number of nested ‘describe()’ statements. I am just going to stick to one per object for the minute.
So I go ahead and add and it block:
it('should have an array of armors with length of 5 armors', function(done){...
});
I am not sure I need the ‘done’ parameter in the callback function, but the code that uses it in the Mocha docs looks nicer so I use it.
Consulting the shouldjs module docs for assertions I look for
- how to test that something is an array.
- how to test the length of said array.
The syntax is very nice and almost human readable:
Armor.armors.should.be.a.Array;
Armor.armors.should.have.lengthOf(5);
Great stuff, except I forgot to call ‘done()’ so my test times out. The finished initial test:
describe('Armor', function(){ it('should have an array of armors with length of 5 armors', function(done){Armor.armors.should.be.a.Array;
Armor.armors.should.have.lengthOf(5);
done();
});
});
The other two properties are functions, shouldjs of course has the ability to test for functions in the same way as it does for arrays:
it('should have a function of getArmorDamageModifier', function(done){ Armor.should.have.property('getArmorDamageModifier');Armor.getArmorDamageModifier.should.be.a.Function;
done();
});
it('should have a function of getArmorName', function(done){ Armor.should.have.property('getArmorName');Armor.getArmorName.should.be.a.Function;
done();
});
All very easy so far, if a little verbose at the minute.
Now that I have tests to determine the outer structure of the object is correct, what about its functionality. I need to verfiy that
- it is possible to call getArmorDamageModifier on every item in the armors array.
- it is possible to call getArmorName on every item in the armors array.
- getArmorDamageModifer returns a number.
- getArmorName returns a string.
So back to plain old javascript for the looping, execute the tests inside the loop, call done() when the loop finishes:
it('should have a property of armorDamageModifier on every object which returns a number', function(done){ for(var item in Armor.armors) { Armor.armors[item].should.have.property ('armorDamageModifier');Armor.getArmorDamageModifier(item).should.be.a.Number;
}
done();
});
it('should have a property of armorName on every object which returns a string', function(done){ for(var item in Armor.armors) { Armor.armors[item].should.have.property('armorName');Armor.getArmorName(item).should.be.a.String;
}
done();
});
I guess I could have combined the test into one loop but it feels to me like I should test each property separately. Also I didn’t test for the properties being functions as I am calling those functions in step two so the test will fail if they are not functions anyway. Maybe later I will find that it is better to have a specific function test which may well give better errors but for now it seems redundant.
Non-static objects
Note: I may not be using static and non-static 100% correctly here and I apologise in advance for my lack of knowledge.
The previous example is addressing an object that I believe is static as it always returns the same thing if you call it with the same parameters, for instance Armors.armors[0].getArmorName() will always return “Plain Clothes”. It is also an object literal that is being returned as part of the module. Some of my other objects are functions with methods attached to their prototype. Back when I was using the module.exports = function(){return something} I started trying to test my function objects as modules but I got into a right state. This is what led me to find the module.exports = something pattern. I cannot even remember now the oddness I experienced trying to get these functions to be testable but now I have it sorted out.
So for the smallest of these I have:
function rpgConsole(id) {
this.content = [];
this.writeCount = 0;
this.id = id;
this.output = '';
}
rpgConsole.prototype.log = function (theClass, theString) { this.writeCount = this.content.unshift({"theClass": theClass, "theText": theString}); var outputTemplate = '<p class="{theClass}">{theText}</p>'; for (var a in this.content[0]) { outputTemplate = outputTemplate.replace('{' + a + '}', this.content[0][a]);}
this.output = outputTemplate + this.output;
};
rpgConsole.prototype.display = function () {
document.getElementById(this.id).innerHTML = this.output;
};
And by adding:
if (typeof module != 'undefined' && module.exports) {module.exports = rpgConsole;
}
At the bottom I get to use it as a module that is instantiated with the new keyword. I am not claiming this is best, just that I do not have to change a whole bunch of code to test it.
I add a constructor to my test inside the describe block but before any it clauses:
var rc = new rpgConsole('test');Initially I decide to test that the object that is constructed has the default values. The only difference to any of the code above is that some of these properties have initial values. shouldjs will take a second parameter for the .property() function, which is rather handily a value to test against the property:
it('should have a property of id which is set to the string "test"', function(done){rc.id.should.be.a.String;
rc.should.have.property('id', 'test');done();
});
As of writing htis I have noticed that I have a slight redundancy in that I effectively test if the property is a String twice, once with the explicit String test and again by passing in a string as the second parameter to the property function. I may remove this later as it is in conflict with my previous statement about the Armor object.
Because this object has methods that change its properties I will need to further test that:
- when the log function is called the writeCount is incremented.
- when the log function is called the arguments appear in the content object.
- when the log function is called the output property is changed to contain the expected string of HTML.
I am not going to test the display method as it affects the DOM and I am not sure that is a unit test scope, or how to do that at the minute.
At this point I think I should maybe have branched out into a different describe statement block but for the minute I will carry on as I am. From now on the tests must be run in the correct order which I am sure is going to work but I think maybe testing the methods in their own block with some set up and tear down may be a better approach.
The test:
it('should have a write count of 1 and a content length of 1 if the log function is executed', function(done){ rc.log('theClass', 'theString'); rc.should.have.property('writeCount', 1);rc.content.should.have.lengthOf(1);
done();
});
The method is called against the object and the new values tested. I am beginning to mix things up a bit here, I have called a method which affects 3 distinct objects but I am only testing two of them. I also have these two tests following that one:
it('should have a content item with a property "theClass" which is set to "theClass" and a property of "theText" which is set to "theString"', function(done){ rc.content[0].should.have.property('theClass', 'theClass'); rc.content[0].should.have.property('theText', 'theString');done();
});
it('should have a property output which is set to <p class="theClass">theString</p>', function(done){ rc.output.should.equal('<p class="theClass">theString</p>'); done();
});
The second two test are trying to test something that was set up in the first test. If the first test fails because the method doesn’t work then these will automatically fail. I will need to refactor these for robustness and general common sense, even putting all of these in the same it block would be nicer but then my test description statement could get very long. I will revisit this after some further reading and add some nested describes which test the object structure separately from the object functionality.
Conlusion
Overall for someone who has been daunted by the idea of setting up testing on my own it has been a revelation of simplicity. As a front-end developer so much of my work happens in the DOM and requires some form of browser testing, normally Selenium based (which often has large dependency and installation overheads) that I don’t get round to it. This exercise has allowed me to get into the actual coding of tests without too much set up, and made me think a little bit more about the structure of my code.