Test Driven JavaScript Development: An Introduction

| 7 min. (1414 words)

The Raygun team is made up of a bunch of nerdy folk just like yourself. Occasionally they write about tools they like and thoughts they have about dev-type topics. This week we’ve got Rick, our longboarding-freediving-Swedish speaking front-end dev talking about test driven JavaScript development. 

Test driven development has been around for a long time in the world of software development. The last few years JavaScript has been growing bigger and websites have become more JavaScript heavy and complex. If you you haven’t done JavaScript testing but are curious, it can be a bit of a jungle to get started. This will be a brief guide to get you started.

Why and when should I test my app?

Writing tests should not be a big project itself. It should be part of your workflow for cases that make sense. What makes sense is hard to say but a rule of thumb could be:

  • Is the functionality complex?
  • Is it an important functionality for the app?
  • Is it easy to write a test?

If you answered yes on all three you should definitely write a test.

There are many reasons why we should be writing tests. Quite often we write code before we are 100% sure of what it should do. We write, learn, rewrite, learn more and rewrite again. Writing a test before the actual development can give you a deeper understanding of the code you are about to write.  It will now be easy to step by step write the code you need and you will be sure it does the right thing throughout the process.

Things you can test

A test could answer the following questions. If you implement it to your build processes you will be sure it always works.

Users

  • Can I create a new user?
  • Can I create a new user with different user types?
  • Does this user type have access to all features and pages they should?
  • Is this user type blocked from features and pages they shouldn’t have access to?
  • Does the user get notified before their credit card expires?
  • Can the new user log in?

Did my CSS changes break anything?

It’s easy to break CSS but sometimes hard to find the broken parts. Testing is a great thing to do.

Build in screen shots to your test, compare them with working images and get a warning if they differ.

Asynchronous requests

  • Does the server return the right data?
  • Does the app behave as expected on slow ajax request?

Links and buttons

  • Do my links and form submits behave as expected?
  • Does my form get the right messages for invalid fields.

As you can tell there are many things that can be worth to test. Now it’s up to you what you want to do with it.

Different test styles

  • TDD (Test Driven Development)
  • BDD (Behavior Driven Development)

They can appear to be quite similar but some key differences are worth mentioning. BDD puts focus on the syntax to achieve a more readable structure. The idea is to write tests in the way you would normally read. It’s also more focused on the team rather than just the developers.  That doesn’t mean BDD is always better, even if some would claim that. Before you choose you should learn more about the differences and also take in account what your team is most familiar with. This is a good explanatory video about BDD and TDD.

Different Frameworks For Test Driven Develpment

I will cover npm test packages since that works for everyone no matter what language or server you work on/with. Make sure you have Node.js installed first.

Here’s a  list of 46 frameworks from designyourway.net.

Yes it’s hard to get an overview of what to use. A good indicator is what other developers are using. Let’s have a look at npm.com’s download history.

Last month download history from npm

  • Mocha 2,009,678 downloads
  • Jasmine 415,922 downloads
  • QUnit 17,479 downloads
  • Buster.JS 9,833 downloads
  • Casper 663 downloads

I will focus on Mocha (since it’s the most common) test suite.

Hook it up to Chai to control assertions

And a browser framwork phantomjs to test browser specific behaviors.

What is Mocha?

Mocha is a well used test framework. It runs on Node.js and is great for asynchronous testing.

What is Chai?

Chai is a BDD / TDD assertion library for node. In my later examples I will show how Mocha uses Chai’s set of assertion styles and how to switch between them. Here’s some test examples to different assertion styles. They’re all testing the same things.

Assert

var assert = require('chai').assert,
  foo = 'bar',
  beverages = { tea: [ 'chai', 'matcha', 'oolong' ] };

assert.typeOf(foo, 'string'); // without optional message
assert.typeOf(foo, 'string', 'foo is a string'); // with optional message
assert.equal(foo, 'bar', 'foo equal `bar`');
assert.lengthOf(foo, 3, 'foo`s value has a length of 3');
assert.lengthOf(beverages.tea, 3, 'beverages has 3 types of tea');

Expect

var expect = require('chai').expect,
  foo = 'bar',
  beverages = { tea: [ 'chai', 'matcha', 'oolong' ] };

expect(foo).to.be.a('string');
expect(foo).to.equal('bar');
expect(foo).to.have.length(3);
expect(beverages).to.have.property('tea').with.length(3);

Should

var should = require('chai').should(), //actually call the function
  foo = 'bar',
  beverages = { tea: [ 'chai', 'matcha', 'oolong' ] };

foo.should.be.a('string');
foo.should.equal('bar');
foo.should.have.length(3);
beverages.should.have.property('tea').with.length(3);

What is PhantomJS?

PhantomJS is a console based browser. It runs on the webkit engine and allows you to do everything you can in a browser but controlled from your console. It comes in handy when we do user behaviour tests. If you want to test it with Firefox Gecko engine try slimerjs.org

Install Mocha, Chai and Phantom

First we need to make sure we have node.js installed. Then we can install…

Mocha.  Since it’s a global installation you might need to sudo before npm.

$ npm install -g mocha

Then Chai

$ npm install chai --save-dev

And PhantomJS

$ npm install -g phantomjs

Or mocha-phantom that I use. It does some background work for easier implementation. 

$ npm install -g mocha-phantomjs

Set up the first test

What we will test is initialization of the single app library Marionette.

  • Is Marionette defined ?
  • Can I create an instance of my marionette object?
  • Does my marionette object have a model attached?

First we need to import libraries, frameworks and npm packages that we need in our test. We will create an HTML file to where we will point the test to. I call mine testrunner.html and it looks like this.

<html>
  <head>
    <meta charset="utf-8">
    <link rel="stylesheet" href="../node_modules/mocha/mocha.css" />
  </head>
  <body>
    <div id="mocha"></div>
    <script src="../node_modules/mocha/mocha.js"></script>
    <script src="../node_modules/chai/chai.js"></script>
    <script src="./jQuery.js"></script>
    <script src="./underscore.js"></script>
    <script src="./backbone.js"></script>
    <script src="./backbone.marionette.min.js"></script>
    <script src="./my-app.js"></script>
    <script>
      mocha.ui('bdd');
      mocha.reporter('html');
      expect = chai.expect;
    </script>
    <script src="test-spec.js"></script>
    <script>
      if (window.mochaPhantomJS) { mochaPhantomJS.run(); }
      else { mocha.run(); }
    </script>
  </body>
</html>

Note that it has a stylesheet. It’s because you can run this file in a regular browser as well. We are also embedding Mocha and Chai for the test functionality. Then the scripts we use in our app e.g jQuery, Underscore, Backbone, Marionette and of course our app scripts (my-app.js).

The next step is the settings. Note that this is where we choose what assert style we want. In this case we are using Chai’s expect style.

My Marionette Application looks like following.

var ItemView = Marionette.ItemView.extend({
  el: 'css-selector',
  template: 'css-selector',
  ui: {
  name: 'css-selector'       
  },
  events: {
    'click @ui.name ': 'callback '
  },
  callback : function(e) {
    console.log('I clicked the button!');
  }
});

var BackboneModel = Backbone.Model.extend({
  url: 'users.php'
});

My test spec

describe('App setup', function(){

	describe('Marionette', function(){

		it('Should be an object', function(){
			expect(Backbone.Marionette).to.be.an('object');
		})

		context('ItemView', function(){
			var itemView = new ItemView( {model: new BackboneModel });

			it('Should have itemView', function(){
				expect(itemView).to.exist;
			})

			it('Should have a backbone module', function(){
				expect(itemView.model).to.be.an('object');
			})
		});
	});
});

It should be pretty self explanatory what the code does. So lets run it.

$ mocha-phantomjs test/testrunner.html

And my console output looks like this.

We’re now getting a clean list of things that work in our app. If something fails we’ll get more detailed information about it.

Summary

I hope this brief guide gave you a good start and made you curious.  As mentioned, there’s a fair bit of libraries and you often end up chaining a few of them to get the features you need.

Have a think about what value you can get and use the internet to become a pro. You, your work mates and users will soon find great value in test driven development.

For more insights into your code, try Raygun free and be alerted of errors before your users walk away in frustration.