Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Unit Testing JavaScript Applications

Stephen Thomas
February 09, 2014

Unit Testing JavaScript Applications

Stephen Thomas

February 09, 2014
Tweet

More Decks by Stephen Thomas

Other Decks in Technology

Transcript

  1. http://github.com/sathomas/jsunittest
    [email protected]
    icon-comment-alt
    icon-comments
    icon-comments-alt
    icon-credit-card
    icon-dashboard
    icon-download
    icon-download-alt
    icon-edit
    icon-envelope
    icon-envelope-alt
    icon-leaf
    icon-legal
    icon-lemon
    icon-lock
    icon-unlock
    icon-magic
    icon-magnet
    icon-map-marker
    icon-minus
    icon-minus-sign
    icon-resize-vertica
    icon-retweet
    icon-road
    icon-rss
    icon-screenshot
    icon-search
    icon-share
    icon-share-alt
    icon-shopping-car
    http://speakerdeck.com/sathomas
    Directional Icons
    icon-arrow-down
    icon-arrow-left
    icon-arrow-right
    icon-arrow-up
    icon-chevron-down
    icon-circle-arrow-down
    icon-circle-arrow-left
    icon-circle-arrow-right
    icon-circle-arrow-up
    icon-chevron-left
    icon-car
    icon-car
    icon-car
    icon-car
    icon-ch
    Video Player Icons
    icon-play-circle
    icon-play
    icon-step-backward
    icon-fast-backward
    icon-fas
    icon-ste
    Unit Testing JavaScript
    Applications
    Stephen Thomas

    View Slide

  2. Finding Bugs
    is a Bitch!

    View Slide

  3. What If We
    Could
    “Childproof”
    Our Code?

    View Slide

  4. Continuous
    Test Driven
    Development
    Block bugs from
    getting into our
    code at all.

    View Slide

  5. 1. Frictionless testing
    during
    development
    2. Re-use of tests
    for integration
    3. Coverage reports


    View Slide

  6. 1. Frictionless testing
    during
    development
    2. Re-use of tests
    for integration
    3. Coverage reports

    Test’Em: Test
    Development
    Environment
    Mocha+: Command
    line execution
    Blanket.js: Coverage
    analysis

    View Slide

  7. Example Application

    View Slide

  8. 1. Test Development
    Environment

    View Slide

  9. Test’Em Configuration
    {
    "framework": "mocha",
    "src_files": [
    "node_modules/jquery/dist/jquery.js",
    "node_modules/underscore/underscore.js",
    "node_modules/backbone/backbone.js",
    "node_modules/chai/chai.js",
    "node_modules/sinon/pkg/sinon.js",
    "src/*.js",
    "test/*.js"
    ]
    }

    View Slide

  10. Test Driven Development
    1. Write a Test
    2. Verify That It Fails
    3. Make It Pass
    4. Repeat As Necessary

    View Slide

  11. First Test
    describe("Application", function(){
    it("creates global variable…", function(){
    should.exist(todoApp);
    })
    })

    View Slide

  12. Passing Code
    if (typeof todoApp === "undefined") {
    todoApp = {};
    }

    View Slide

  13. View Slide

  14. View Slide

  15. View Slide

  16. Testing a Model
    describe("Todo Model", function(){
    describe("Initialization", function(){
    beforeEach(function(){
    this.todo = new todoApp.Todo();
    })
    it("should default status…", function(){
    this.todo.get('complete').should.be.false;
    })
    it("should default title…", function(){
    this.todo.get('title').should.equal("");
    })
    })

    View Slide

  17. Make The Tests Pass
    todoApp.Todo = Backbone.Model.extend({
    defaults: {
    title: "",
    complete: false
    }
    })

    View Slide

  18. Testing a View
    describe("Todo List Item View", function(){
    beforeEach(function(){
    this.todo = new todoApp.Todo({
    title: "Todo"});
    this.item = new todoApp.TodoListItem({
    model: this.todo});
    })
    it("render() should return view", function(){
    this.item.render().should.equal(this.item);
    });
    it("should render as list item", function(){
    this.item.render()
    .el.nodeName.should.equal("LI");
    })

    View Slide

  19. Stubs
    describe("Todo Model", function(){
    describe("Attributes", function(){
    beforeEach(function(){
    this.todo = new todoApp.Todo();
    this.stb = sinon.stub(this.todo,"save");
    })
    afterEach(function(){
    this.stb.restore();
    })
    it(“should set title attr…", function(){
    this.todo.set('title',"Test");
    this.todo.get('title')
    .should.equal("Test");
    })

    View Slide

  20. Mocking a REST API
    describe("REST API", function(){
    it("should load via the API", function(){
    this.ajax = sinon.stub($,"ajax")

    .yieldsTo("success", [
    {id:1, title:"Mock1", complete:false}
    ]);
    this.todos = new todoApp.Todos();
    this.todos.fetch();
    this.todos.should.have.length(1);
    this.todos.at(0).get('title')
    .should.equal("Mock1");
    this.ajax.restore();
    })
    })

    View Slide

  21. Mocha Tip #1: Never
    “Comment Out” a Test
    describe("Todo Model", function(){
    describe("Initialization", function(){
    beforeEach(function(){
    this.todo = new todoApp.Todo();
    })
    it("should default…", function(){
    this.todo.get('complete').should.be.false;
    })

    View Slide

  22. Mocha Tip #1: Never
    “Comment Out” a Test
    describe("Todo Model", function(){
    describe("Initialization", function(){
    beforeEach(function(){
    this.todo = new todoApp.Todo();
    })
    it.skip("should default…", function(){
    this.todo.get('complete').should.be.false;
    })

    View Slide

  23. Mocha Tip #1: Never
    “Comment Out” a Test
    describe("Todo Model", function(){
    describe("Initialization", function(){
    beforeEach(function(){
    this.todo = new todoApp.Todo();
    })
    it.skip("should default…", function(){
    this.todo.get('complete').should.be.false;
    })

    View Slide

  24. View Slide

  25. Mocha Tip #1b: Skip an
    Entire Test Block
    describe.skip("Todo Model", function(){
    describe("Initialization", function(){
    beforeEach(function(){
    this.todo = new todoApp.Todo();
    })
    it("should default…", function(){
    this.todo.get('complete').should.be.false;
    })

    View Slide

  26. Mocha Tip #2:

    Focus on a Single Test
    describe("Todo Model", function(){
    describe("Initialization", function(){
    beforeEach(function(){
    this.todo = new todoApp.Todo();
    })
    it.only("should default…", function(){
    this.todo.get('complete').should.be.false;
    })

    View Slide

  27. 2. Command-Line Test
    Execution

    View Slide

  28. Test Code Setup
    if (typeof exports !== 'undefined' &&

    this.exports !== exports) {
    var jsdom = require("jsdom").jsdom;
    var doc=jsdom("");
    var window = doc.createWindow();
    var $ = require("jquery")(window);
    global._ = require("underscore");
    global.Backbone = require("backbone");
    Backbone.$ = $;
    var chai = require("chai");
    var sinon = require("sinon");
    }

    View Slide

  29. Mocha Options:

    mocha.opts
    test/app-todos-test.js
    src/app-todos.js

    View Slide

  30. Run Mocha

    View Slide

  31. 3. Test Coverage

    View Slide

  32. Install Blanket.js

    View Slide

  33. Generate Coverage Report

    View Slide

  34. View Slide

  35. View Slide

  36. Resources
    • Test Development Environment
    Test’em: https://github.com/airportyh/testem
    • Command Line JavaScript Execution Environment
    node.js: http://nodejs.org
    • JavaScript Unit Testing Framework
    Mocha: http://visionmedia.github.io/mocha/
    • JavaScript Assertion Library
    Chai: http://chaijs.com
    • Spies, Stubs, and Mocks
    Sinon.JS: http://sinonjs.org
    • Test Coverage
    Blanket.js: http://blanketjs.org

    View Slide