As some of you might know, I’ve already written a lot of posts about unit testing in PHP. But what about your JavaScript? That might require testing too. Especially when it comes to critical functions that involve complex operations. One road you could take to do this is use a headless browser and a testing framework like CasperJS, or go full blown with something like Selenium.
But most of these solutions can become quite heavy and cumbersome to set up. What if you want to keep it a bit more small and simple? Well the other way I was experimenting with a different approach to write tests for my JavaScript.
Mocha
One great test framework for JavaScript is MochaJS. According to their website:
Mocha is a feature-rich JavaScript test framework running on Node.js and the browser, making asynchronous testing simple and fun.
Installation is simple. As with any Node package, you simple install it with:
1 |
npm install -g mocha |
Your now ready to write your tests with Mocha. To read more about this, I’d recommend reading their documentation. Although Mocha has a way of running a test suite in your browser, I’m going to take a different approach on this one.
Instead of including Mocha in our HTML and running the test in our browser, I’m going to show you how you can test your websites’ JavaScript with Mocha from the command line.
Write your JavaScript properly
One important aspect in order to get this to work, is that you should write your JavaScript properly. When testing your JavaScript from the Shell, we’re actually parsing it with Node, so you’d better write it object oriented. I’ve wrote an article about this a while back on how to do this. A basic class could look something like this:
1 2 3 4 5 6 7 8 |
function SomeClass() { // constructor }; SomeClass.prototype.doStuff = function(param) { // Some stuff happens here // Return something }; |
This is a very basic example of a class.
Keep the methods that you want to test separate
Needless to say, this method of writing tests can’t cover all aspects of web development. For example, manipulation to the DOM or other web-specific objects simply aren’t possible because we run our test from the shell with Node and not in the browser. Unless you want to go headless with something like Mocha-PhantomJS this is something to keep in mind while you are writing your code.
I’ve already written some articles about this for unit testing in PHP. The idea is very simple: store the methods with the heavy number crunching and the stuff you want to test in their own methods so they can be separately tested outside the scope of the global program. After all, that’s the pure essence of a unit test: you’re testing a single unit.
Let’s pretend we have a real-life JavaScript web application, that needs to validate some form values prior before submitting. The initial setup of the class could look something 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 30 31 32 33 34 |
function FormValidator(formId) { this.form = document.getElementById(formId); this.form.addEventListener('submit', this.validate.bind(this)); this.validationErrors = []; }; /* * Validate the form * @param {Event} e */ FormValidator.prototype.validate = function(e) { this.validationErrors = []; if ( this.validateEmailAddress() && this.validatePhoneNumber() && this.validateAccountNumber() ) { this.showLoader(); } else { e.preventDefault(); this.showValidationError(); } }; /* * Validate the e-mail address * @return {boolean} */ FormValidator.prototype.validateEmailAddress = function() { var address = this.form.querySelector('input.email').value; // validate the e-mail address and return boolean } // etc ... |
This code is untestable for us, because all of the functions have references to native functionality of the web browser. Therefore we cannot test (run) these functions in Node. To summarise these methods:
- document.getElementById()
- querySelector()
- preventDefault()
Now let’s have a close look at this code. If we look closely, most of the code is pretty straight-forward. Actually, the most important functionality of this class are the validation functions. Those functions require testing. Now let’s set our code up a little different:
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 32 33 34 35 36 37 |
function FormValidator(formId) { this.form = document.getElementById(formId); this.emailField = this.form.querySelector('.email'); this.phoneField = this.form.querySelector('.phone'); this.accountField = this.form.querySelector('.account'); this.form.addEventListener('submit', this.validate.bind(this)); this.validationErrors = []; }; /* * Validate the form * @param {Event} e */ FormValidator.prototype.validate = function(e) { this.validationErrors = []; if ( this.isValidEmailAddress(this.emailField.value) && this.isValidPhoneNumber(this.phoneField.value) && this.isValidAccountNumber(this.accountField.value) ) { this.showLoader(); } else { e.preventDefault(); this.showValidationError(); } }; /* * Validate an e-mail address * @param {string} address * @return {boolean} */ FormValidator.prototype.isValidEmailAddress = function(address) { // validate e-mail address and return boolean } // etc ... |
See what I did there? Instead of using a parameter-less method for validation, I gave our validation methods a parameter, which is used by the method. This way, our isValidEmailAddress() -method doesn’t have to know about the global scope of the website. The method is completely self-contained and testable.
Separate files for separate classes
To test separate classes with Node our classes need to be saved in separate files. The way I do this: I already write my JavaScript in separate files (one file for each JavaScript class). Whilst developing, I watch my JavaScript files for changes and automatically with Gulp and concatenate them if any of them changes. This way, my website only needs to load one JavaScript file, but I still have the relaxing comfort of keeping everything organised. A more detailed explanation of how to do this is described in this article.
Write a simple test
Now for the fun part: how can we write a test in Mocha that loads the same JavaScript as our website does? Well, since our JavaScript is already object oriented, and we’re separating our classes in separate files, we can simply make use of Nodes’ module.exports -property to export the same JavaScript we use on our website to Node.
There is one drawback though, we can’t simply put module.exports below each and every JavaScript file, because – logically – this would throw errors in our browser. Instead, let’s just wrap them in a small hack so that it only ‘works’ in Node. Add this below your class:
1 2 3 4 5 |
if (typeof process !== 'undefined' && typeof module !== 'undefined') { if(/node/.test(process.title)) { module.exports = FormValidator; } } |
Of course, you need to replace FormValidator with the name of your class.
Voila! Now our class is ready to get loaded into Node and tested with Mocha. Let me show you a simple test:
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 |
var assert = require('assert'); var FormValidator = require('../path/to/formvalidator.js'); describe('My Test Suite', function() { describe('FormValidator',function() { var formValidator = new FormValidator(); // Prefix test: describe(':: isValidEmailAddress()', function(){ it('should return true', function(){ assert.equal(formValidator.isValidEmailAddress( 'foo@bar.com'), true); // etc... }); it('should return false', function(){ assert.equal(formValidator.isValidEmailAddress( 'foo@bar'), false); // etc... }); }); }); }); |
And there you have it! Now you can write unit tests with Mocha with the same JavaScript that is used in the browser, without having to include Mocha into your HTML.
In conclusion
Unit testing is a great tool for developers to write more tested and better maintainable code. The possibilities with Node and Mocha to write unit tests for your websites’ JavaScript are great, but not endless. After all, using this method you can only test JavaScript methods that are the ‘number crunchers’, but not the methods that are responsible for DOM manipulation.
If you need to test the DOM, well you can’t escape from including Mocha into your HTML, or going for the headless-browser route. There’s nothing wrong with that of course, both methods have their benefits and drawbacks.
I would like to finish this article by stating that I’m also fairly new to MochaJS. I’ve worked with other testing frameworks like PHPUnit and CasperJS, so I used my experience and way-of-working from those products into Mocha. If you have any thoughts or reactions on this article, perhaps even a way to debunk most of what I stated in this article, please feel free to use the comment form below this article. After all, I’m not just blogging to share but also to learn.
Visitors give this article an average rating of 4.8 out of 5.
How would you rate this article?
★ ★ ★ ★ ★