Your SlideShare is downloading. ×
0
Unit Testing for Great Justiceby Domenic Denicola                             @domenic
Domenic Denicola@domenichttps://npmjs.org/profile/domenicdenicolahttps://github.com/domenichttps://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 codethat invokes a function and then checksassumptions about its logical behavior.   ...
// Arrangevar excerpt = "A unit test is an automated piece of code.";var highlights = [{ start: 2, length: 4, color: "yell...
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’veseen comes from this guyhttp://butunclebob.com/ArticleS.UncleBob.TheSensitivityProblem  ...
“Software is a very sensitive domain. If a single bit of a100MB executable is wrong, the entire application canbe brought ...
“Accountants solved this problem long ago. They use aset of practices and disciplines that reduce theprobability that erro...
“We in software have a similar mechanism that providesa first line of defense: Test Driven Development (TDD).Every intenti...
ok, but why unit test all the things?                                        @domenic
function highlight(excerpt, highlights) {    if (highlights.length === 0) {        return excerpt;    }    if (highlightsO...
more generally:        A     C                EInput                                      Output        B     D           ...
you also getconfidencethe ability to refactor without fearcredibilityfree documentation                               ...
https://gist.github.com/305ad492c2fd20c466be                   @domenichttps://github.com/senchalabs/connect/blob/gh-pages...
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.                                          @dom...
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 t...
this is why we use layered architecture the domain model only depends on itself the domain services only depend on the m...
testing the domain model is easy// Arrangevar shelf = new Shelf();var book = { id: "123" };shelf.addBook(book);// Actvar h...
spies: a gentle introduction// Arrangevar shelf = new Shelf();var book = { id: "123" };var spy = sinon.spy();shelf.on("boo...
bdd: an even gentler introductionhttps://gist.github.com/3399842                                    @domenic
testing services is harderdownloader.download(book) when the app is offline    it should callback with a “no internet” e...
when the app is offline, it shouldcallback with a “no internet” error// Arrangevar downloader = new Downloader();var book ...
untestable Downloaderfunction Downloader() {    this.download = function (book, cb) {         if (!navigator.onLine) {    ...
dependency injection to the rescue!function Downloader(isOnline) {    this.download = function (book, cb) {         if (!i...
app code becomes:var downloader = new Downloader(function () { return navigator.onLine; });                               ...
test code becomes:// Arrangefunction isOnline() { return false; }var downloader = new Downloader(isOnline);var book = { id...
similarly:function Downloader(isOnline, drmService, doDownloadAjax) {    this.download = function (book, cb) {         // ...
testing ui is much like testing services, but      now you depend on the dom                                           @do...
testing ui codevar TodoView = Backbone.View.extend({      // ... lots of stuff omitted ...      events: {           "dblcl...
testing ui code: bad testsetupEntireApplication();addATodo();var $todo = $("#todos > li").first();$todo.find("label").dblc...
testing ui code: good testvar todoView = new TodoView();todoView.$el = $(document.createElement("div"));todoView.input = {...
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 (highlightsO...
 when given a highlight and an excerpt   it should return the excerpt with highlighting tags inserted                   ...
var excerpt = "A unit test is an automated piece of code.";var highlights = [{ start: 2, length: 4, color: "yellow" }];var...
✓ function highlight(excerpt, highlights) {◌     if (highlights.length === 0) {✗         return excerpt;✓     }✓◌     if (...
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. ...
the three steps of tdd red green refactor                         @domenic
 when there are no highlights    the excerpt should pass through unchanged                0/1 tests passed              ...
function highlight(excerpt, highlights) {    if (highlights.length === 0) {        return excerpt;    }}                  ...
✓ function highlight(excerpt, highlights) {◌     if (highlights.length === 0) {✓         return excerpt;✓     }✓ }        ...
✓ 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 highli...
✓ function highlight(excerpt, highlights) {✓     if (highlights.length === 0) {✓         return excerpt;✓     }✓✓     var ...
 when there are no highlights    the excerpt should pass through unchanged when there are simple non-overlapping highli...
✓ function highlight(excerpt, highlights) {✓     if (highlights.length === 0) {✓         return excerpt;✓     }✓✓     if (...
@domenic
✓ function highlight(excerpt, highlights) {✓     if (highlightsOverlap(highlights)) {✓         highlights = subdivideHighl...
summary Unit tests are automated tests that verify your application’s logic by  breaking it up into small units. Unit te...
unit-testing tools i like Mocha test runner: http://mochajs.com Chai assertion library: http://chaijs.com Sinon.JS spy/...
Upcoming SlideShare
Loading in...5
×

Unit Testing for Great Justice

2,349

Published on

The discovery of unit testing and test-driven development was one of the most important parts of my growth as a developer. The ability to write simple, small pieces of code that could verify the behavior of my application was in itself quite useful. And the ability to refactor without fear, just by running the test suite, changed how I program. But the real benefits come in how unit tests shape your application code: more testable code is often more well thought-out, more decoupled, and more extensible.

In this talk, I'll give a whirlwind introduction to unit testing as a concept and as a practice. I want you fully convinced it's the best thing to happen to software development, if you aren't already. Once we're on the same page there, I'll take a deep dive into what makes a good unit test. This involves testing tools such as spies, stubs, and mocks, concepts like code coverage, and practices like dependency injection that shape your application code. The most important lesson will be on how to focus on singular, isolated units of code in your testing, as this guides you toward building modular, flexible, and comprehensible applications.

Published in: Technology
1 Comment
0 Likes
Statistics
Notes
  • Be the first to like this

No Downloads
Views
Total Views
2,349
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
31
Comments
1
Likes
0
Embeds 0
No embeds

No notes for slide

Transcript of "Unit Testing for Great Justice"

  1. 1. Unit Testing for Great Justiceby Domenic Denicola @domenic
  2. 2. Domenic Denicola@domenichttps://npmjs.org/profile/domenicdenicolahttps://github.com/domenichttps://github.com/NobleJS
  3. 3. q: how do you know your code works?a: it doesn’t. @domenic
  4. 4. to make sure something works, you need to test it. @domenic
  5. 5. but not manually @domenic
  6. 6. two levels of automated testingintegration testingunit testing @domenic
  7. 7. @domenic
  8. 8. today we’re talking about unit testing:whatwhyhowwhen @domenic
  9. 9. what is a unit test? @domenic
  10. 10. q: what is a unit?a: a single function or method @domenic
  11. 11. A unit test is an automated piece of codethat invokes a function and then checksassumptions about its logical behavior. @domenic
  12. 12. // Arrangevar excerpt = "A unit test is an automated piece of code.";var highlights = [{ start: 2, length: 4, color: "yellow" }];// Actvar result = highlight(excerpt, highlights);// Assertexpect(result).to.equal(A <span class="highlight yellow"> + unit</span> test is an automated + piece of code.); @domenic
  13. 13. q: how big should a unit be?a: about ten lines @domenic
  14. 14. unit tested functions will:do one thingdo it correctly @domenic
  15. 15. q: what code should you unit test?a: all the code (that has logic) @domenic
  16. 16. @domenic
  17. 17. why unit test all the things? @domenic
  18. 18. the most compelling reasoning i’veseen comes from this guyhttp://butunclebob.com/ArticleS.UncleBob.TheSensitivityProblem @domenic
  19. 19. “Software is a very sensitive domain. If a single bit of a100MB executable is wrong, the entire application canbe brought to its knees. Very few other domains suffersuch extreme sensitivity to error. But one very importantdomain does: accounting. A single digit error in amassive pile of spreadsheets and financial statementscan cost millions and bankrupt an organization.” @domenic
  20. 20. “Accountants solved this problem long ago. They use aset of practices and disciplines that reduce theprobability that errors can go undetected. One of thesepractices is Dual Entry Bookkeeping. Every transaction isentered twice; once in the credit books, and once in thedebit books. The two entries participate in very differentcalculations but eventually result in a final result of zero.That zero means that the all the entries balance. Thestrong implication is that there are no single digit errors.” @domenic
  21. 21. “We in software have a similar mechanism that providesa first line of defense: Test Driven Development (TDD).Every intention is entered in two places: once in a unittest, and once in the production code. These two entriesfollow very different pathways, but eventually sum to agreen bar. That green bar means that the two intentsbalance, i.e. the production code agrees with the tests.” @domenic
  22. 22. ok, but why unit test all the things? @domenic
  23. 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. 24. more generally: A C EInput Output B D F http://stackoverflow.com/a/11917341/3191 @domenic
  25. 25. you also getconfidencethe ability to refactor without fearcredibilityfree documentation @domenic
  26. 26. https://gist.github.com/305ad492c2fd20c466be @domenichttps://github.com/senchalabs/connect/blob/gh-pages/tests.md
  27. 27. and most importantly, you get testable code. @domenic
  28. 28. how do i write testable code? @domenic
  29. 29. the most important thing to remember: your tests should only test your code. @domenic
  30. 30. corollary: in the end, it’s all about managing dependencies @domenic
  31. 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. 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. 33. testing the domain model is easy// Arrangevar shelf = new Shelf();var book = { id: "123" };shelf.addBook(book);// Actvar hasBook = shelf.hasBook("123");// Assertexpect(hasBook).to.be.true; @domenic
  34. 34. spies: a gentle introduction// Arrangevar shelf = new Shelf();var book = { id: "123" };var spy = sinon.spy();shelf.on("bookAdded", spy);// Actshelf.addBook(book);// Assert @domenicexpect(spy).to.have.been.calledWith(book);
  35. 35. bdd: an even gentler introductionhttps://gist.github.com/3399842 @domenic
  36. 36. testing services is harderdownloader.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. 37. when the app is offline, it shouldcallback with a “no internet” error// Arrangevar downloader = new Downloader();var book = { id: "123" };// ??? how to set up "app is offline"?// Actdownloader.download(book, function (err) { // Assert expect(err).to.exist.and.have.property("message", "No internet!"); done();}); @domenic
  38. 38. untestable Downloaderfunction Downloader() { this.download = function (book, cb) { if (!navigator.onLine) { cb(new Error("No internet!")); return; } // ... };} @domenic
  39. 39. dependency injection to the rescue!function Downloader(isOnline) { this.download = function (book, cb) { if (!isOnline()) { cb(new Error("No internet!")); return; } // ... };} @domenic
  40. 40. app code becomes:var downloader = new Downloader(function () { return navigator.onLine; }); @domenic
  41. 41. test code becomes:// Arrangefunction isOnline() { return false; }var downloader = new Downloader(isOnline);var book = { id: "123" };// … @domenic
  42. 42. similarly:function Downloader(isOnline, drmService, doDownloadAjax) { this.download = function (book, cb) { // https://gist.github.com/3400303 };} @domenic
  43. 43. testing ui is much like testing services, but now you depend on the dom @domenic
  44. 44. testing ui codevar 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. 45. testing ui code: bad testsetupEntireApplication();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. 46. testing ui code: good testvar 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. 47. when should i write my tests? @domenic
  48. 48. let’s talk about code coverage @domenic
  49. 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. 50.  when given a highlight and an excerpt  it should return the excerpt with highlighting tags inserted @domenic
  51. 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. 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. 53. q: how can we achieve 100% coverage?a: use test-driven development @domenic
  54. 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. 55. the three steps of tdd red green refactor @domenic
  56. 56.  when there are no highlights  the excerpt should pass through unchanged 0/1 tests passed @domenic
  57. 57. function highlight(excerpt, highlights) { if (highlights.length === 0) { return excerpt; }} @domenic
  58. 58. ✓ function highlight(excerpt, highlights) {◌ if (highlights.length === 0) {✓ return excerpt;✓ }✓ } @domenic
  59. 59. ✓ function highlight(excerpt, highlights) {✓ return excerpt;✓ } 1/1 tests passed @domenic
  60. 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. 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. 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. 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✓ }
  64. 64. @domenic
  65. 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. 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. 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
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×