SlideShare a Scribd company logo
Unit Testing for Great Justice
by Domenic Denicola




                             @domenic
Domenic Denicola
@domenic
https://npmjs.org/profile/domenicdenicola
https://github.com/domenic
https://github.com/NobleJS
q: how do you know your code works?
a: it doesn’t.




                                       @domenic
to make sure something works,
     you need to test it.



                           @domenic
but not manually




                   @domenic
two levels of automated testing


integration testing
unit testing



                                  @domenic
@domenic
today we’re talking about unit testing:


what
why
how
when

                                     @domenic
what is a unit test?




                       @domenic
q: what is a unit?
a: a single function or method




                                  @domenic
A unit test is an automated piece of code
that invokes a function and then checks
assumptions about its logical behavior.




                                          @domenic
// Arrange
var excerpt = "A unit test is an automated piece of code.";
var highlights = [{ start: 2, length: 4, color: "yellow" }];


// Act
var result = highlight(excerpt, highlights);


// Assert
expect(result).to.equal('A <span class="highlight yellow">' +
                           'unit</span> test is an automated ' +
                           'piece of code.');                      @domenic
q: how big should a unit be?
a: about ten lines




                                @domenic
unit tested functions will:
do one thing
do it correctly




                              @domenic
q: what code should you unit test?
a: all the code (that has logic)




                                      @domenic
@domenic
why unit test all the things?




                                @domenic
the most compelling reasoning i’ve
seen comes from this guy




http://butunclebob.com/ArticleS.UncleBob.TheSensitivityProblem   @domenic
“Software is a very sensitive domain. If a single bit of a
100MB executable is wrong, the entire application can
be brought to it's knees. Very few other domains suffer
such extreme sensitivity to error. But one very important
domain does: accounting. A single digit error in a
massive pile of spreadsheets and financial statements
can cost millions and bankrupt an organization.”




                                                             @domenic
“Accountants solved this problem long ago. They use a
set of practices and disciplines that reduce the
probability that errors can go undetected. One of these
practices is Dual Entry Bookkeeping. Every transaction is
entered twice; once in the credit books, and once in the
debit books. The two entries participate in very different
calculations but eventually result in a final result of zero.
That zero means that the all the entries balance. The
strong implication is that there are no single digit errors.”
                                                            @domenic
“We in software have a similar mechanism that provides
a first line of defense: Test Driven Development (TDD).
Every intention is entered in two places: once in a unit
test, and once in the production code. These two entries
follow very different pathways, but eventually sum to a
green bar. That green bar means that the two intents
balance, i.e. the production code agrees with the tests.”




                                                           @domenic
ok, but why unit test all the things?




                                        @domenic
function highlight(excerpt, highlights) {
    if (highlights.length === 0) {
        return excerpt;
    }


    if (highlightsOverlap(highlights)) {
        highlights = subdivideHighlights(highlights);
    }


    var tags = makeTags(highlights);
    var highlighted = insertTags(excerpt, tags);


    return highlighted;
                                                        @domenic

}
more generally:




        A     C                E
Input                                      Output

        B     D                F




                  http://stackoverflow.com/a/11917341/3191
                                                      @domenic
you also get


confidence
the ability to refactor without fear
credibility
free documentation



                                        @domenic
https://gist.github.com/305ad492c2fd20c466be                   @domenic

https://github.com/senchalabs/connect/blob/gh-pages/tests.md
and most importantly, you get
       testable code.




                                @domenic
how do i write testable code?




                                @domenic
the most important thing to remember:
 your tests should only test your code.




                                          @domenic
corollary: in the end, it’s all about
    managing dependencies




                                        @domenic
this is why we use mv* patterns

 the model is all your code: no dependencies
 the view has no logic: no need to test it
 the controller (or whatever) has simple logic and is easy to test using fakes




                                                                           @domenic
this is why we use layered architecture

 the domain model only depends on itself
 the domain services only depend on the models
 the application services only depend on the domain
 the infrastructure code is straightforward translation: easy to test
 the ui code just ties together application services and views




                                                                         @domenic
testing the domain model is easy

// Arrange
var shelf = new Shelf();
var book = { id: "123" };
shelf.addBook(book);


// Act
var hasBook = shelf.hasBook("123");


// Assert
expect(hasBook).to.be.true;

                                      @domenic
spies: a gentle introduction

// Arrange
var shelf = new Shelf();
var book = { id: "123" };


var spy = sinon.spy();
shelf.on("bookAdded", spy);


// Act
shelf.addBook(book);


// Assert
                                             @domenic
expect(spy).to.have.been.calledWith(book);
bdd: an even gentler introduction

https://gist.github.com/3399842




                                    @domenic
testing services is harder

downloader.download(book)
 when the app is offline
    it should callback with a “no internet” error
 when the app is online
    and the DRM service says the user has run out of licenses
        it should callback with a “no licenses left” error
    and the DRM service says the user can download on this
     computer
        and the download succeeds
            it should callback with no error, and the book text
        and the download fails
            it should callback with the underlying error

                                                                   @domenic
when the app is offline, it should
callback with a “no internet” error
// Arrange
var downloader = new Downloader();
var book = { id: "123" };
// ??? how to set up "app is offline"?


// Act
downloader.download(book, function (err) {
      // Assert
      expect(err).to.exist.and.have.property("message", "No internet!");
      done();
});                                                                        @domenic
untestable Downloader

function Downloader() {
    this.download = function (book, cb) {
         if (!navigator.onLine) {
             cb(new Error("No internet!"));
             return;
         }


         // ...
    };
}
                                              @domenic
dependency injection to the rescue!

function Downloader(isOnline) {
    this.download = function (book, cb) {
         if (!isOnline()) {
             cb(new Error("No internet!"));
             return;
         }


         // ...
    };
}
                                              @domenic
app code becomes:

var downloader = new Downloader(function () { return navigator.onLine; });




                                                                        @domenic
test code becomes:

// Arrange
function isOnline() { return false; }
var downloader = new Downloader(isOnline);
var book = { id: "123" };


// …




                                             @domenic
similarly:

function Downloader(isOnline, drmService, doDownloadAjax) {
    this.download = function (book, cb) {
         // https://gist.github.com/3400303
    };
}




                                                              @domenic
testing ui is much like testing services, but
      now you depend on the dom




                                           @domenic
testing ui code
var TodoView = Backbone.View.extend({
      // ... lots of stuff omitted ...


      events: {
           "dblclick label": "edit"
      },


      edit: function () {
           this.$el.addClass("editing");
           this.input.focus();
      }
});                                                                                                   @domenic


                    https://github.com/addyosmani/todomvc/blob/master/architecture-examples/backbone/js/views/todos.js
testing ui code: bad test
setupEntireApplication();
addATodo();
var $todo = $("#todos > li").first();


$todo.find("label").dblclick();


expect($todo.hasClass("editing")).to.be.true;
expect(document.activeElement).to.equal($todo.find(".edit")[0]);




                                                                   @domenic
testing ui code: good test
var todoView = new TodoView();
todoView.$el = $(document.createElement("div"));
todoView.input = { focus: sinon.spy() };


todoView.edit();


expect(todoView.$el.hasClass("editing")).to.be.true;
expect(todoView.input.focus).to.have.been.called;




                                                       @domenic
when should i write my tests?




                                @domenic
let’s talk about code coverage




                                 @domenic
function highlight(excerpt, highlights) {
    if (highlights.length === 0) {
        return excerpt;
    }


    if (highlightsOverlap(highlights)) {
        highlights = subdivideHighlights(highlights);
    }


    var tags = makeTags(highlights);
    var highlighted = insertTags(excerpt, tags);


    return highlighted;
                                                        @domenic

}
 when given a highlight and an excerpt
   it should return the excerpt with highlighting tags inserted




                                                                   @domenic
var excerpt = "A unit test is an automated piece of code.";
var highlights = [{ start: 2, length: 4, color: "yellow" }];




var result = highlight(excerpt, highlights);




expect(result).to.equal('A <span class="highlight yellow">' +
                           'unit</span> test is an automated ' +
                           'piece of code.');                      @domenic
✓ function highlight(excerpt, highlights) {
◌     if (highlights.length === 0) {
✗         return excerpt;
✓     }
✓
◌     if (highlightsOverlap(highlights)) {
✗         highlights = subdivideHighlights(highlights);
✓     }
✓
✓     var tags = makeTags(highlights);
✓     var highlighted = insertTags(excerpt, tags);
✓
✓     return highlighted;
                                                          @domenic

✓ }
q: how can we achieve 100% coverage?
a: use test-driven development




                                   @domenic
the three rules of tdd

 You are not allowed to write any production code unless
  it is to make a failing unit test pass.
 You are not allowed to write any more of a unit test than
  is sufficient to fail.
 You are not allowed to write any more production code
  than is sufficient to pass the one failing unit test.




           http://butunclebob.com/ArticleS.UncleBob.TheThreeRulesOfTdd
                                                                 @domenic
the three steps of tdd

 red
 green
 refactor




                         @domenic
 when there are no highlights
    the excerpt should pass through unchanged




                0/1 tests passed                 @domenic
function highlight(excerpt, highlights) {
    if (highlights.length === 0) {
        return excerpt;
    }
}




                                            @domenic
✓ function highlight(excerpt, highlights) {
◌     if (highlights.length === 0) {
✓         return excerpt;
✓     }
✓ }




                                              @domenic
✓ function highlight(excerpt, highlights) {
✓     return excerpt;
✓ }




                        1/1 tests passed      @domenic
 when there are no highlights
    the excerpt should pass through unchanged
 when there are simple non-overlapping highlights
    it should insert tags around those areas




                  1/2 tests passed                   @domenic
✓ function highlight(excerpt, highlights) {
✓     if (highlights.length === 0) {
✓         return excerpt;
✓     }
✓
✓     var tags = makeTags(highlights);
✓     var highlighted = insertTags(excerpt, tags);
✓
✓     return highlighted;
✓ }




                        2/2 tests passed             @domenic
 when there are no highlights
    the excerpt should pass through unchanged
 when there are simple non-overlapping highlights
    it should insert tags around those substrings
 when there are overlapping highlights
    it should subdivide them before inserting the tags




                  2/3 tests passed                        @domenic
✓ function highlight(excerpt, highlights) {
✓     if (highlights.length === 0) {
✓         return excerpt;
✓     }
✓
✓     if (highlightsOverlap(highlights)) {
✓         highlights = subdivideHighlights(highlights);
✓     }
✓
✓     var tags = makeTags(highlights);
✓     var highlighted = insertTags(excerpt, tags);
✓
✓     return highlighted;
                         3/3 tests passed                 @domenic

✓ }
@domenic
✓ function highlight(excerpt, highlights) {
✓     if (highlightsOverlap(highlights)) {
✓         highlights = subdivideHighlights(highlights);
✓     }
✓
✓     var tags = makeTags(highlights);
✓     var highlighted = insertTags(excerpt, tags);
✓
✓     return highlighted;
✓ }




                       3/3 tests still passing!           @domenic
summary

 Unit tests are automated tests that verify your application’s logic by
  breaking it up into small units.
 Unit testing is like double-entry bookkeeping. It gives you the ability
  to refactor without fear.
 Writing unit tests will lead to writing testable code, which is
  decoupled via dependency injection and thus becomes more
  modular, flexible, and comprehensible.
 The best way to write unit tests is with test-driven development,
  which has three steps: red, green, refactor. Make these steps as
  small as possible.


                                                                     @domenic
unit-testing tools i like

 Mocha test runner: http://mochajs.com
 Chai assertion library: http://chaijs.com
 Sinon.JS spy/stub/mock library: http://sinonjs.org
 Sandboxed-Module environment faker: http://npm.im/sandboxed-module
 Cover code coverage tool: http://npm.im/cover
 My Chai plugins:
     Sinon–Chai: http://npm.im/sinon-chai
     Chai as Promised: http://npm.im/chai-as-promised




                                                                @domenic

More Related Content

KEY
Working Effectively With Legacy Code
PPT
Php Debugger
PPT
Writing Pluggable Software
PDF
Living With Legacy Code
PDF
Keeping the frontend under control with Symfony and Webpack
PPT
Reliable acceptance testing
PDF
Getting big without getting fat, in perl
ODP
Bring the fun back to java
Working Effectively With Legacy Code
Php Debugger
Writing Pluggable Software
Living With Legacy Code
Keeping the frontend under control with Symfony and Webpack
Reliable acceptance testing
Getting big without getting fat, in perl
Bring the fun back to java

What's hot (20)

DOC
Perl web programming
ODP
Modern Web Development with Perl
PDF
Lunch and learn: Cucumber and Capybara
PDF
Design patterns revisited with PHP 5.3
ODP
Introduction to Web Programming with Perl
PDF
High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014
PPT
Apache Ant
PDF
The quest for global design principles (SymfonyLive Berlin 2015)
DOC
Debugging over tcp and http
PDF
Flask patterns
PPTX
Test automation with Cucumber-JVM
PDF
InterConnect: Java, Node.js and Swift - Which, Why and When
KEY
How To Test Everything
PDF
Always up to date, testable and maintainable documentation with OpenAPI
PPT
Intoduction to Play Framework
PDF
Fighting Fear-Driven-Development With PHPUnit
PDF
Jumping Into WordPress Plugin Programming
PDF
Workshop quality assurance for php projects - phpdublin
PDF
Web develop in flask
PPT
TDD, BDD, RSpec
Perl web programming
Modern Web Development with Perl
Lunch and learn: Cucumber and Capybara
Design patterns revisited with PHP 5.3
Introduction to Web Programming with Perl
High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014
Apache Ant
The quest for global design principles (SymfonyLive Berlin 2015)
Debugging over tcp and http
Flask patterns
Test automation with Cucumber-JVM
InterConnect: Java, Node.js and Swift - Which, Why and When
How To Test Everything
Always up to date, testable and maintainable documentation with OpenAPI
Intoduction to Play Framework
Fighting Fear-Driven-Development With PHPUnit
Jumping Into WordPress Plugin Programming
Workshop quality assurance for php projects - phpdublin
Web develop in flask
TDD, BDD, RSpec
Ad

Similar to Unit Testing for Great Justice (20)

PDF
Having Fun with Play
PDF
Understanding the Node.js Platform
KEY
Ruby For Startups
PDF
Intro To JavaScript Unit Testing - Ran Mizrahi
PPT
Capistrano Overview
KEY
[Coscup 2012] JavascriptMVC
PDF
Serverless in production, an experience report (Going Serverless)
PPT
Capistrano
PDF
Cannibalising The Google App Engine
PDF
How Testability Inspires AngularJS Design / Ran Mizrahi
PPTX
JavaScript on the Desktop
PDF
Serverless in production, an experience report (linuxing in london)
PDF
End-to-end web-testing in ruby ecosystem
PPTX
Electron - cross platform desktop applications made easy
PDF
Choosing a Javascript Framework
DOCX
Getting Started with Maven and Cucumber in Eclipse
PDF
Serverless in production, an experience report (JeffConf)
PDF
Automatisation in development and testing - within budget
ODP
RichFaces - Testing on Mobile Devices
PDF
DCEU 18: App-in-a-Box with Docker Application Packages
Having Fun with Play
Understanding the Node.js Platform
Ruby For Startups
Intro To JavaScript Unit Testing - Ran Mizrahi
Capistrano Overview
[Coscup 2012] JavascriptMVC
Serverless in production, an experience report (Going Serverless)
Capistrano
Cannibalising The Google App Engine
How Testability Inspires AngularJS Design / Ran Mizrahi
JavaScript on the Desktop
Serverless in production, an experience report (linuxing in london)
End-to-end web-testing in ruby ecosystem
Electron - cross platform desktop applications made easy
Choosing a Javascript Framework
Getting Started with Maven and Cucumber in Eclipse
Serverless in production, an experience report (JeffConf)
Automatisation in development and testing - within budget
RichFaces - Testing on Mobile Devices
DCEU 18: App-in-a-Box with Docker Application Packages
Ad

More from Domenic Denicola (20)

PPTX
The State of JavaScript (2015)
PPTX
Async Frontiers
PPTX
The jsdom
PPTX
The Final Frontier
PPTX
ES6 in Real Life
PPTX
Streams for the Web
PPTX
After Return of the Jedi
PPTX
The State of JavaScript
PPTX
How to Win Friends and Influence Standards Bodies
PPTX
The Extensible Web
PPTX
The Promised Land (in Angular)
PDF
ES6: The Awesome Parts
PDF
Boom! Promises/A+ Was Born
PPTX
PPTX
Client-Side Packages
PPTX
Creating Truly RESTful APIs
PPTX
Promises, Promises
PPTX
ES6 is Nigh
PPTX
Real World Windows 8 Apps in JavaScript
PPTX
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
The State of JavaScript (2015)
Async Frontiers
The jsdom
The Final Frontier
ES6 in Real Life
Streams for the Web
After Return of the Jedi
The State of JavaScript
How to Win Friends and Influence Standards Bodies
The Extensible Web
The Promised Land (in Angular)
ES6: The Awesome Parts
Boom! Promises/A+ Was Born
Client-Side Packages
Creating Truly RESTful APIs
Promises, Promises
ES6 is Nigh
Real World Windows 8 Apps in JavaScript
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...

Recently uploaded (20)

PDF
Mobile App Security Testing_ A Comprehensive Guide.pdf
PDF
KodekX | Application Modernization Development
PDF
Agricultural_Statistics_at_a_Glance_2022_0.pdf
PDF
Encapsulation theory and applications.pdf
PDF
Electronic commerce courselecture one. Pdf
PDF
Shreyas Phanse Resume: Experienced Backend Engineer | Java • Spring Boot • Ka...
DOCX
The AUB Centre for AI in Media Proposal.docx
PDF
Reach Out and Touch Someone: Haptics and Empathic Computing
PPTX
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
PDF
Machine learning based COVID-19 study performance prediction
PPTX
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
PDF
Dropbox Q2 2025 Financial Results & Investor Presentation
PDF
NewMind AI Monthly Chronicles - July 2025
PDF
cuic standard and advanced reporting.pdf
PDF
Diabetes mellitus diagnosis method based random forest with bat algorithm
PDF
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
PDF
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
PPTX
Digital-Transformation-Roadmap-for-Companies.pptx
PPTX
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
PDF
Bridging biosciences and deep learning for revolutionary discoveries: a compr...
Mobile App Security Testing_ A Comprehensive Guide.pdf
KodekX | Application Modernization Development
Agricultural_Statistics_at_a_Glance_2022_0.pdf
Encapsulation theory and applications.pdf
Electronic commerce courselecture one. Pdf
Shreyas Phanse Resume: Experienced Backend Engineer | Java • Spring Boot • Ka...
The AUB Centre for AI in Media Proposal.docx
Reach Out and Touch Someone: Haptics and Empathic Computing
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
Machine learning based COVID-19 study performance prediction
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
Dropbox Q2 2025 Financial Results & Investor Presentation
NewMind AI Monthly Chronicles - July 2025
cuic standard and advanced reporting.pdf
Diabetes mellitus diagnosis method based random forest with bat algorithm
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
Digital-Transformation-Roadmap-for-Companies.pptx
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
Bridging biosciences and deep learning for revolutionary discoveries: a compr...

Unit Testing for Great Justice

  • 1. Unit Testing for Great Justice by Domenic Denicola @domenic
  • 3. q: how do you know your code works? a: it doesn’t. @domenic
  • 4. to make sure something works, you need to test it. @domenic
  • 5. but not manually @domenic
  • 6. two levels of automated testing integration testing unit testing @domenic
  • 8. today we’re talking about unit testing: what why how when @domenic
  • 9. what is a unit test? @domenic
  • 10. q: what is a unit? a: a single function or method @domenic
  • 11. A unit test is an automated piece of code that invokes a function and then checks assumptions about its logical behavior. @domenic
  • 12. // Arrange var excerpt = "A unit test is an automated piece of code."; var highlights = [{ start: 2, length: 4, color: "yellow" }]; // Act var result = highlight(excerpt, highlights); // Assert expect(result).to.equal('A <span class="highlight yellow">' + 'unit</span> test is an automated ' + 'piece of code.'); @domenic
  • 13. q: how big should a unit be? a: about ten lines @domenic
  • 14. unit tested functions will: do one thing do it correctly @domenic
  • 15. q: what code should you unit test? a: all the code (that has logic) @domenic
  • 17. why unit test all the things? @domenic
  • 18. the most compelling reasoning i’ve seen comes from this guy http://butunclebob.com/ArticleS.UncleBob.TheSensitivityProblem @domenic
  • 19. “Software is a very sensitive domain. If a single bit of a 100MB executable is wrong, the entire application can be brought to it's knees. Very few other domains suffer such extreme sensitivity to error. But one very important domain does: accounting. A single digit error in a massive pile of spreadsheets and financial statements can cost millions and bankrupt an organization.” @domenic
  • 20. “Accountants solved this problem long ago. They use a set of practices and disciplines that reduce the probability that errors can go undetected. One of these practices is Dual Entry Bookkeeping. Every transaction is entered twice; once in the credit books, and once in the debit books. The two entries participate in very different calculations but eventually result in a final result of zero. That zero means that the all the entries balance. The strong implication is that there are no single digit errors.” @domenic
  • 21. “We in software have a similar mechanism that provides a first line of defense: Test Driven Development (TDD). Every intention is entered in two places: once in a unit test, and once in the production code. These two entries follow very different pathways, but eventually sum to a green bar. That green bar means that the two intents balance, i.e. the production code agrees with the tests.” @domenic
  • 22. ok, but why unit test all the things? @domenic
  • 23. function highlight(excerpt, highlights) { if (highlights.length === 0) { return excerpt; } if (highlightsOverlap(highlights)) { highlights = subdivideHighlights(highlights); } var tags = makeTags(highlights); var highlighted = insertTags(excerpt, tags); return highlighted; @domenic }
  • 24. more generally: A C E Input Output B D F http://stackoverflow.com/a/11917341/3191 @domenic
  • 25. you also get confidence the ability to refactor without fear credibility free documentation @domenic
  • 26. https://gist.github.com/305ad492c2fd20c466be @domenic https://github.com/senchalabs/connect/blob/gh-pages/tests.md
  • 27. and most importantly, you get testable code. @domenic
  • 28. how do i write testable code? @domenic
  • 29. the most important thing to remember: your tests should only test your code. @domenic
  • 30. corollary: in the end, it’s all about managing dependencies @domenic
  • 31. this is why we use mv* patterns  the model is all your code: no dependencies  the view has no logic: no need to test it  the controller (or whatever) has simple logic and is easy to test using fakes @domenic
  • 32. this is why we use layered architecture  the domain model only depends on itself  the domain services only depend on the models  the application services only depend on the domain  the infrastructure code is straightforward translation: easy to test  the ui code just ties together application services and views @domenic
  • 33. testing the domain model is easy // Arrange var shelf = new Shelf(); var book = { id: "123" }; shelf.addBook(book); // Act var hasBook = shelf.hasBook("123"); // Assert expect(hasBook).to.be.true; @domenic
  • 34. spies: a gentle introduction // Arrange var shelf = new Shelf(); var book = { id: "123" }; var spy = sinon.spy(); shelf.on("bookAdded", spy); // Act shelf.addBook(book); // Assert @domenic expect(spy).to.have.been.calledWith(book);
  • 35. bdd: an even gentler introduction https://gist.github.com/3399842 @domenic
  • 36. testing services is harder downloader.download(book)  when the app is offline  it should callback with a “no internet” error  when the app is online  and the DRM service says the user has run out of licenses  it should callback with a “no licenses left” error  and the DRM service says the user can download on this computer  and the download succeeds  it should callback with no error, and the book text  and the download fails  it should callback with the underlying error @domenic
  • 37. when the app is offline, it should callback with a “no internet” error // Arrange var downloader = new Downloader(); var book = { id: "123" }; // ??? how to set up "app is offline"? // Act downloader.download(book, function (err) { // Assert expect(err).to.exist.and.have.property("message", "No internet!"); done(); }); @domenic
  • 38. untestable Downloader function Downloader() { this.download = function (book, cb) { if (!navigator.onLine) { cb(new Error("No internet!")); return; } // ... }; } @domenic
  • 39. dependency injection to the rescue! function Downloader(isOnline) { this.download = function (book, cb) { if (!isOnline()) { cb(new Error("No internet!")); return; } // ... }; } @domenic
  • 40. app code becomes: var downloader = new Downloader(function () { return navigator.onLine; }); @domenic
  • 41. test code becomes: // Arrange function isOnline() { return false; } var downloader = new Downloader(isOnline); var book = { id: "123" }; // … @domenic
  • 42. similarly: function Downloader(isOnline, drmService, doDownloadAjax) { this.download = function (book, cb) { // https://gist.github.com/3400303 }; } @domenic
  • 43. testing ui is much like testing services, but now you depend on the dom @domenic
  • 44. testing ui code var TodoView = Backbone.View.extend({ // ... lots of stuff omitted ... events: { "dblclick label": "edit" }, edit: function () { this.$el.addClass("editing"); this.input.focus(); } }); @domenic https://github.com/addyosmani/todomvc/blob/master/architecture-examples/backbone/js/views/todos.js
  • 45. testing ui code: bad test setupEntireApplication(); addATodo(); var $todo = $("#todos > li").first(); $todo.find("label").dblclick(); expect($todo.hasClass("editing")).to.be.true; expect(document.activeElement).to.equal($todo.find(".edit")[0]); @domenic
  • 46. testing ui code: good test var todoView = new TodoView(); todoView.$el = $(document.createElement("div")); todoView.input = { focus: sinon.spy() }; todoView.edit(); expect(todoView.$el.hasClass("editing")).to.be.true; expect(todoView.input.focus).to.have.been.called; @domenic
  • 47. when should i write my tests? @domenic
  • 48. let’s talk about code coverage @domenic
  • 49. function highlight(excerpt, highlights) { if (highlights.length === 0) { return excerpt; } if (highlightsOverlap(highlights)) { highlights = subdivideHighlights(highlights); } var tags = makeTags(highlights); var highlighted = insertTags(excerpt, tags); return highlighted; @domenic }
  • 50.  when given a highlight and an excerpt  it should return the excerpt with highlighting tags inserted @domenic
  • 51. var excerpt = "A unit test is an automated piece of code."; var highlights = [{ start: 2, length: 4, color: "yellow" }]; var result = highlight(excerpt, highlights); expect(result).to.equal('A <span class="highlight yellow">' + 'unit</span> test is an automated ' + 'piece of code.'); @domenic
  • 52. ✓ function highlight(excerpt, highlights) { ◌ if (highlights.length === 0) { ✗ return excerpt; ✓ } ✓ ◌ if (highlightsOverlap(highlights)) { ✗ highlights = subdivideHighlights(highlights); ✓ } ✓ ✓ var tags = makeTags(highlights); ✓ var highlighted = insertTags(excerpt, tags); ✓ ✓ return highlighted; @domenic ✓ }
  • 53. q: how can we achieve 100% coverage? a: use test-driven development @domenic
  • 54. the three rules of tdd  You are not allowed to write any production code unless it is to make a failing unit test pass.  You are not allowed to write any more of a unit test than is sufficient to fail.  You are not allowed to write any more production code than is sufficient to pass the one failing unit test. http://butunclebob.com/ArticleS.UncleBob.TheThreeRulesOfTdd @domenic
  • 55. the three steps of tdd  red  green  refactor @domenic
  • 56.  when there are no highlights  the excerpt should pass through unchanged 0/1 tests passed @domenic
  • 57. function highlight(excerpt, highlights) { if (highlights.length === 0) { return excerpt; } } @domenic
  • 58. ✓ function highlight(excerpt, highlights) { ◌ if (highlights.length === 0) { ✓ return excerpt; ✓ } ✓ } @domenic
  • 59. ✓ function highlight(excerpt, highlights) { ✓ return excerpt; ✓ } 1/1 tests passed @domenic
  • 60.  when there are no highlights  the excerpt should pass through unchanged  when there are simple non-overlapping highlights  it should insert tags around those areas 1/2 tests passed @domenic
  • 61. ✓ function highlight(excerpt, highlights) { ✓ if (highlights.length === 0) { ✓ return excerpt; ✓ } ✓ ✓ var tags = makeTags(highlights); ✓ var highlighted = insertTags(excerpt, tags); ✓ ✓ return highlighted; ✓ } 2/2 tests passed @domenic
  • 62.  when there are no highlights  the excerpt should pass through unchanged  when there are simple non-overlapping highlights  it should insert tags around those substrings  when there are overlapping highlights  it should subdivide them before inserting the tags 2/3 tests passed @domenic
  • 63. ✓ function highlight(excerpt, highlights) { ✓ if (highlights.length === 0) { ✓ return excerpt; ✓ } ✓ ✓ if (highlightsOverlap(highlights)) { ✓ highlights = subdivideHighlights(highlights); ✓ } ✓ ✓ var tags = makeTags(highlights); ✓ var highlighted = insertTags(excerpt, tags); ✓ ✓ return highlighted; 3/3 tests passed @domenic ✓ }
  • 65. ✓ function highlight(excerpt, highlights) { ✓ if (highlightsOverlap(highlights)) { ✓ highlights = subdivideHighlights(highlights); ✓ } ✓ ✓ var tags = makeTags(highlights); ✓ var highlighted = insertTags(excerpt, tags); ✓ ✓ return highlighted; ✓ } 3/3 tests still passing! @domenic
  • 66. summary  Unit tests are automated tests that verify your application’s logic by breaking it up into small units.  Unit testing is like double-entry bookkeeping. It gives you the ability to refactor without fear.  Writing unit tests will lead to writing testable code, which is decoupled via dependency injection and thus becomes more modular, flexible, and comprehensible.  The best way to write unit tests is with test-driven development, which has three steps: red, green, refactor. Make these steps as small as possible. @domenic
  • 67. unit-testing tools i like  Mocha test runner: http://mochajs.com  Chai assertion library: http://chaijs.com  Sinon.JS spy/stub/mock library: http://sinonjs.org  Sandboxed-Module environment faker: http://npm.im/sandboxed-module  Cover code coverage tool: http://npm.im/cover  My Chai plugins:  Sinon–Chai: http://npm.im/sinon-chai  Chai as Promised: http://npm.im/chai-as-promised @domenic