Unit Testing

Single Page Applications

By @johnkpaul

johnkpaul.com/html5devs

github.com/johnkpaul/twitter-show-more-example

Unit Testing - Confidence

Domenic did an amazing job. I bet he convinced you all.

  • I'm was tired of manually refreshing and manually debugging everything
  • I was tired of not knowing when I've broken something
  • I was tired of using my app to find all the edge cases

Imperative code is easy to test


    function add(){
        var sum = 0;
        for(var i = 0;i<arguments.length;i++){
            sum += arguments[i];
        }
        return sum;
    }
    
    equal(10, add(1, 2, 3, 4), "sum should be 10");
    equal(100, add(50, 50), "sum should be 100");
                                                

refactoring is awesome


    function add(){
        return Array.prototype.slice.call(arguments)
                        .reduce(function(prev, current){
                            return prev + current;
                        });
    }
    
    equal(10, add(1, 2, 3, 4), "sum should be 10");
    equal(100, add(50, 50), "sum should be 100");
                                                

cross browser test runners can verify that your tests pass everywhere

Imagine if you wanted to unit test

But our code doesn't look like that.

Our lives are so much easier with client side MV*

We have modularity

We have "classes"

What to test?

imperative logic & event logic

  • models, collections and views each have a bit of both
  • it's logic we want to test, not every nitty gritty detail
  • views and templates are the most difficult to sort out

Quick App Demo

And then into the code!

First, we'll take a look at the collection

  • ensure we store the last fetched id
  • ensure that last fetched id is sent with every subsequent request
  • ensure that when fetched, it always uses add:true option

QUnit test - sinon ajax example

Collection's fetch method

Now, onto the view

  • ensure that collection is fetched when rendered
  • ensure that collection is fetched periodically for update
  • ensure that subview is created for each model in the collection
  • ensure that new subviews are hidden when models are added to collection
  • ensure that show more button is visible, if there are hidden subviews
  • ensure that when clicking on show more button, hidden subviews are now visible
  • ... and on and on...

collection is fetched when rendered

Complex View test - sinon timer example

Code to pass test

What exactly is a unit?

  • On the client, it's a little blurry
  • How do we deal with units of code with dependencies?
  • is a template separately testable?
  • minimize, but if necessary, integrate some pieces

template conventions

  • .js- and .js-test- class prefixes
  • keep templates out of the DOM for sharing between app and tests

template dependent test

event handling

  • make clean separation between triggering and handling of event
  • in Backbone's case, there is no need to test that view events fire correctly
  • But in the case of custom events, test that they are fired when expected

How do we run all of these tests?

Grunt

Grunt is a task-based command line build tool for JavaScript projects.

grunt-junit

demo

Did I forget the router?

You know what to do

Real confidence

Jenkins

(although any continuous integration system will do)

QUnit alternatives

  • Jasmine - BDD
  • Chai - TDD/BDD
  • BusterJS
  • JSTestDriver

Cross browser testing services

  • Testling
  • Browserling
  • Browserstack

QUESTIONS

By @johnkpaul

www.johnkpaul.com