Your SlideShare is downloading. ×
0
Client-Side Unit Testing                       Cloud Chen                       2012/5/11
You don’t write tests
You know you should,    but you don’t
You won’t be blamed for it
Because...
There are many common issues   that prevent developers       for writing tests
You think
You think•   Tests are not necessary and irrelevant
You think•   Tests are not necessary and irrelevant•   Manual testing is enough
You think•   Tests are not necessary and irrelevant•   Manual testing is enough•   Lacking of specification
You think•   Tests are not necessary and irrelevant•   Manual testing is enough•   Lacking of specification•   Spaghetti co...
You think•   Tests are not necessary and irrelevant•   Manual testing is enough•   Lacking of specification•   Spaghetti co...
You think•   Tests are not necessary and irrelevant•   Manual testing is enough•   Lacking of specification•   Spaghetti co...
You think•   Tests are not necessary and irrelevant•   Manual testing is enough•   Lacking of specification•   Spaghetti co...
Our goal
Our goal  Help you start writing unit testsfor front-end code
However,
Let’s start with another     type of testing
Functional Testing
What is functional testing Functional testing is a type of black box testing that bases its test cases on the specification...
What is functional testing Functional testing is a type of black box testing that bases its test cases on the specification...
In a nutshell
In a nutshell• Written from user perspective
In a nutshell• Written from user perspective• Proving users are able to reproduce defined steps
In a nutshell• Written from user perspective• Proving users are able to reproduce defined steps• Don’t need to consider int...
In a nutshell• Written from user perspective• Proving users are able to reproduce defined steps• Don’t need to consider int...
Unit Testing
What is unit testingunit testing is a method by which individualunits of source code, sets of one or morecomputer program ...
What is unit testingunit testing is a method by which individualunits of source code, sets of one or morecomputer program ...
In a nutshell
In a nutshell• Unit testing is complete isolation
In a nutshell• Unit testing is complete isolation• Must irrelevant to external dependencies
In a nutshell• Unit testing is complete isolation• Must irrelevant to external dependencies• All unreliable or slow depend...
In a nutshell• Unit testing is complete isolation• Must irrelevant to external dependencies• All unreliable or slow depend...
In a nutshell• Unit testing is complete isolation• Must irrelevant to external dependencies• All unreliable or slow depend...
What are differences between functional and unit testing
What are differences between functional and unit testing                    unit            functionalperspective     prog...
What are differences between functional and unit testing                    unit            functionalperspective     prog...
What are differences between functional and unit testing                    unit            functionalperspective     prog...
What are differences between functional and unit testing                    unit            functionalperspective     prog...
What are differences between functional and unit testing                    unit            functionalperspective     prog...
Hence..
Hence..•   We are programmer
Hence..•   We are programmer•   We care our code quality
Hence..•   We are programmer•   We care our code quality•   We should write unit test code
Why, When     ofunit testing
Why need unit testing
Why need unit testing• Ensuring every component is bug-free
Why need unit testing• Ensuring every component is bug-free• Ensuring every component is easy to modify
Why need unit testing• Ensuring every component is bug-free• Ensuring every component is easy to modify• Easy to locating ...
Why need unit testing• Ensuring every component is bug-free• Ensuring every component is easy to modify• Easy to locating ...
Why need unit testing• Ensuring every component is bug-free• Ensuring every component is easy to modify• Easy to locating ...
Why need unit testing• Ensuring every component is bug-free• Ensuring every component is easy to modify• Easy to locating ...
When to do unit testing
When to do unit testing• When you write your own classes, modules,  libraries, frameworks.
When to do unit testing• When you write your own classes, modules,  libraries, frameworks.• You don’t need to write test c...
testing style comparison
testing style comparison                    TDD                    BDD based on      function oriented     feature oriente...
testing style comparison                    TDD                    BDD based on      function oriented     feature oriente...
testing style comparison                    TDD                    BDD based on      function oriented     feature oriente...
testing style comparison                    TDD                    BDD based on      function oriented     feature oriente...
testing style comparison                    TDD                    BDD based on      function oriented     feature oriente...
testing style comparison
testing style comparison• It doesn’t matter which style you choose
testing style comparison• It doesn’t matter which style you choose• It does matter how many cases you have
testing style comparison
testing style comparison As we are using Scrum,
testing style comparison As we are using Scrum, BDD is more suitable for us.
BDD Framework
BDD Framework•   Focuses on assertion, doesn’t depend on DOM
BDD Framework•   Focuses on assertion, doesn’t depend on DOM•   Succinct API
BDD Framework•   Focuses on assertion, doesn’t depend on DOM•   Succinct API•   Natively support Spy
BDD Framework•   Focuses on assertion, doesn’t depend on DOM•   Succinct API•   Natively support Spy•   Support spec helper
BDD Framework•   Focuses on assertion, doesn’t depend on DOM•   Succinct API•   Natively support Spy•   Support spec helpe...
BDD Framework•   Focuses on assertion, doesn’t depend on DOM•   Succinct API•   Natively support Spy•   Support spec helpe...
BDD Framework•   Focuses on assertion, doesn’t depend on DOM•   Succinct API•   Natively support Spy•   Support spec helpe...
BDD Framework•   Focuses on assertion, doesn’t depend on DOM•   Succinct API•   Natively support Spy•   Support spec helpe...
BDD Framework•   Focuses on assertion, doesn’t depend on DOM•   Succinct API•   Natively support Spy•   Support spec helpe...
Typical Spec & Succinct APIdescribe("Flashcard model", function () {    beforeEach(function () {        this.Flashcard = r...
Typical Spec & Succinct APIdescribe("Flashcard model", function () {    beforeEach(function () {        this.Flashcard = r...
Typical Spec & Succinct APIdescribe("Flashcard model", function () {    beforeEach(function () {        this.Flashcard = r...
Typical Spec & Succinct APIdescribe("Flashcard model", function () {    beforeEach(function () {        this.Flashcard = r...
Typical Spec & Succinct APIdescribe("Flashcard model", function () {    beforeEach(function () {        this.Flashcard = r...
Typical Spec & Succinct APIdescribe("Flashcard model", function () {    beforeEach(function () {        this.Flashcard = r...
Typical Spec & Succinct APIdescribe("Flashcard model", function () {    beforeEach(function () {        this.Flashcard = r...
Typical Spec & Succinct APIdescribe("Flashcard model", function () {    beforeEach(function () {        this.Flashcard = r...
Other Native Matchersexpect(x).toEqual(y);expect(x).toBe(y);expect(x).toMatch(pattern);expect(x).toBeDefined();expect(x).t...
Other Native Matchersexpect(x).not.toEqual(y);expect(x).not.toBe(y);expect(x).not.toMatch(pattern);expect(x).not.toBeDefin...
Spec HelperbeforeEach(function() {  this.fixtures = {      Flashcard: {        valid: { // response starts here          "...
Spec HelperbeforeEach(function() {  this.fixtures = {      Flashcard: {        valid: { // response starts here          "...
Spydescribe("Flashcard model", function () {    beforeEach(function () {        this.Flashcard = require(models/Flashcard)...
Spydescribe("Flashcard model", function () {    beforeEach(function () {        this.Flashcard = require(models/Flashcard)...
Spydescribe("Flashcard model", function () {    beforeEach(function () {        this.Flashcard = require(models/Flashcard)...
Spydescribe("Flashcard model", function () {    beforeEach(function () {        this.Flashcard = require(models/Flashcard)...
Spydescribe("Flashcard model", function () {    beforeEach(function () {        this.Flashcard = require(models/Flashcard)...
Spydescribe("Flashcard model", function () {    beforeEach(function () {        this.Flashcard = require(models/Flashcard)...
Extending Matcher<a href=”#” class=”link”>this is a link</a>expect($(‘a’).hasClass(s-selected)).toBeTruthy();   expectatio...
Extending Matcher<a href=”#” class=”link”>this is a link</a>expect($(‘a’).hasClass(s-selected)).toBeTruthy();   expectatio...
Matcher from            jasmine jquery<a href=”#” class=”link”>this is a link</a>expect($(‘a’).hasClass(s-selected)).toBeT...
Matcher from            jasmine jquery<a href=”#” class=”link”>this is a link</a>expect($(‘a’).hasClass(s-selected)).toBeT...
Matcher from            jasmine jquery<a href=”#” class=”link”>this is a link</a>expect($(‘a’).hasClass(s-selected)).toBeT...
Matcher from            jasmine jquery<a href=”#” class=”link”>this is a link</a>expect($(‘a’).hasClass(s-selected)).toBeT...
jasmine-jquery features• a set of custom matchers for jQuery  framework• an API for handling HTML fixtures in your  specs
jasmine-jquery matchersexpect(x).toBeHidden()expect(x).toBeVisible()expect(x).toHaveAttr(attributeName, attributeValue)exp...
jasmine-jquery fixturesIn myfixture.html file:<div id="my-fixture">some complex content here</div>Inside your test:loadFixt...
Running JasmineStandalone Runner    * Manually manage of your project files and specs
Running JasmineDedicated Server runs jasmineRuby jasmine Gem     * Using yaml manages of your project files and specs
Recap rules of Unit Testing• Unit testing is complete isolation• Must irrelevant to external dependencies• All unreliable ...
Recap rules of Unit Testing• Unit testing is complete isolation• Must irrelevant to external dependencies• All unreliable ...
Stub every dependency
Stub every dependency• Native spy feature of Jasmine is not enough
Stub every dependency• Native spy feature of Jasmine is not enough• It doesn’t support fake timer
Stub every dependency• Native spy feature of Jasmine is not enough• It doesn’t support fake timer• It doesn’t support fake...
Stub every dependency• Native spy feature of Jasmine is not enough• It doesn’t support fake timer• It doesn’t support fake...
Stub every dependency• Native spy feature of Jasmine is not enough• It doesn’t support fake timer• It doesn’t support fake...
Spy/Stub/Mock
Spy/Stub/Mock•   A test spy is a function that records arguments, return value,    the value of this and exception thrown ...
Spy/Stub/Mock•   A test spy is a function that records arguments, return value,    the value of this and exception thrown ...
Spy/Stub/Mock•   A test spy is a function that records arguments, return value,    the value of this and exception thrown ...
Spy/Stub/Mock
Spy/Stub/Mock Spy
Spy/Stub/Mock Spy       Stub
Spy/Stub/Mock Spy       Stub              Mock
Spydescribe("when initialized", function () {    beforeEach(function() {        sinon.spy(this, "Flashcards");           c...
Spydescribe("when initialized", function () {    beforeEach(function() {        sinon.spy(this, "Flashcards");    })      ...
Spydescribe("when initialized", function () {    beforeEach(function() {        sinon.spy(this, "Flashcards");    })      ...
Spydescribe("when initialized", function () {    beforeEach(function() {        sinon.spy(this, "Flashcards");    })      ...
Spydescribe("when initialized", function () {    beforeEach(function() {        sinon.spy(this, "Flashcards");    })      ...
Stubdescribe("when update model", function () {    beforeEach(function () {        this.sync = sinon.stub(Backbone, "sync"...
Stubdescribe("when update model", function () {    beforeEach(function () {        this.sync = sinon.stub(Backbone, "sync"...
Stubdescribe("when update model", function () {    beforeEach(function () {        this.sync = sinon.stub(Backbone, "sync"...
Stubdescribe("when update model", function () {    beforeEach(function () {        this.sync = sinon.stub(Backbone, "sync"...
Stub              But, Spy cannot do like so:it("should always confirm every confirmation", function () {    sinon.stub(wi...
Stub              But, Spy cannot do like so:it("should always confirm every confirmation", function () {    sinon.stub(wi...
Stub              But, Spy cannot do like so:it("should always confirm every confirmation", function () {    sinon.stub(wi...
Mock• Mock focuses on implementation details of  one method• It utilizes upfront expectation to verify details  rather tha...
Mockdescribe("when initialized", function () {    it("should throw Error when cultureCode is not passed", function() {    ...
Without FakeTimersit("should show teacher box after 1 hour", function () {    var hour = 1000 * 60 * 60;    setTimeout(sho...
FakeTimersit("should show teacher box after 1 hour", function () {    this.clock = sinon.useFakeTimers();    var hour = 10...
FakeTimersit("should show teacher box after 1 hour", function () {    this.clock = sinon.useFakeTimers();    var hour = 10...
FakeTimersit("should show teacher box after 1 hour", function () {    this.clock = sinon.useFakeTimers();    var hour = 10...
Fake Serverdescribe("Flashcard model", function () {    beforeEach(function () {        this.Flashcard = require(models/Fl...
Fake Serverdescribe("Flashcard model", function () {    beforeEach(function () {        this.Flashcard = require(models/Fl...
Fake Serverdescribe("Flashcard model", function () {    beforeEach(function () {        this.Flashcard = require(models/Fl...
Fake Serverdescribe("Flashcard model", function () {    beforeEach(function () {        this.Flashcard = require(models/Fl...
Fake Serverdescribe("Flashcard model", function () {    beforeEach(function () {        this.Flashcard = require(models/Fl...
Fake Serverdescribe("Flashcard model", function () {    beforeEach(function () {        this.Flashcard = require(models/Fl...
Fake Serverdescribe("Flashcard model", function () {    beforeEach(function () {        this.Flashcard = require(models/Fl...
Fake Serverdescribe("Flashcard model", function () {    beforeEach(function () {        this.Flashcard = require(models/Fl...
Fake Serverdescribe("Flashcard model", function () {    beforeEach(function () {        this.Flashcard = require(models/Fl...
Next Step• fixture management • DOM • HTTP Response• Code Coverage for Javascript• Integration with CI• Integration with js...
Thank you
Thank youif you like this topic please give me
Thank youif you like this topic please give me
Client Side Unit Testing
Client Side Unit Testing
Upcoming SlideShare
Loading in...5
×

Client Side Unit Testing

1,337

Published on

This slide introduces you what unit testing is, differences between functional testing and unit testing. Furthermore, it tells you the concepts about jasmine and sinon.js

Published in: Technology
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

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

No notes for slide
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • &amp;#x518D;&amp;#x5F00;&amp;#x59CB;&amp;#x5355;&amp;#x5143;&amp;#x6D4B;&amp;#x8BD5;&amp;#x4E4B;&amp;#x524D;, &amp;#x5148;&amp;#x4E86;&amp;#x89E3;&amp;#x4E00;&amp;#x4E0B;&amp;#x53E6;&amp;#x5916;&amp;#x4E00;&amp;#x79CD;&amp;#x6D4B;&amp;#x8BD5;. &amp;#x8BA4;&amp;#x6E05;&amp;#x5404;&amp;#x79CD;&amp;#x7C7B;&amp;#x578B;&amp;#x6D4B;&amp;#x8BD5;&amp;#x4E4B;&amp;#x95F4;&amp;#x7684;&amp;#x5DEE;&amp;#x522B;\n
  • \n
  • \n
  • \n
  • &amp;#x4F7F;&amp;#x624B;&amp;#x52A8;&amp;#x6D4B;&amp;#x8BD5;&amp;#x81EA;&amp;#x52A8;&amp;#x5316;\n
  • &amp;#x4F7F;&amp;#x624B;&amp;#x52A8;&amp;#x6D4B;&amp;#x8BD5;&amp;#x81EA;&amp;#x52A8;&amp;#x5316;\n
  • &amp;#x4F7F;&amp;#x624B;&amp;#x52A8;&amp;#x6D4B;&amp;#x8BD5;&amp;#x81EA;&amp;#x52A8;&amp;#x5316;\n
  • &amp;#x4F7F;&amp;#x624B;&amp;#x52A8;&amp;#x6D4B;&amp;#x8BD5;&amp;#x81EA;&amp;#x52A8;&amp;#x5316;\n
  • \n
  • &amp;#x5355;&amp;#x5143;&amp;#x6D4B;&amp;#x8BD5;&amp;#x662F;&amp;#x4E00;&amp;#x79CD;&amp;#x65B9;&amp;#x6CD5;, &amp;#x6E90;&amp;#x7801;&amp;#x4E2D;&amp;#x72EC;&amp;#x7ACB;&amp;#x7684;&amp;#x5355;&amp;#x5143;\n
  • &amp;#x6CE8;&amp;#x610F;&amp;#x72EC;&amp;#x7ACB;&amp;#x7684;&amp;#x5355;&amp;#x5143;&amp;#x5E76;&amp;#x4E0D;&amp;#x4E00;&amp;#x5B9A;&amp;#x662F;&amp;#x5355;&amp;#x72EC;&amp;#x7684;&amp;#x65B9;&amp;#x6CD5;, \n
  • &amp;#x5FC5;&amp;#x987B;&amp;#x548C;&amp;#x5916;&amp;#x90E8;&amp;#x4F9D;&amp;#x8D56;&amp;#x5173;&amp;#x7CFB;&amp;#x4E0D;&amp;#x76F8;&amp;#x5173;, &amp;#x4FDD;&amp;#x6301;&amp;#x5355;&amp;#x5143;&amp;#x7684;&amp;#x72EC;&amp;#x7ACB;&amp;#x6027;, &amp;#x662F;&amp;#x4E3A;&amp;#x4E86;&amp;#x4FDD;&amp;#x8BC1;&amp;#x9519;&amp;#x8BEF;&amp;#x4E0D;&amp;#x662F;&amp;#x7531;&amp;#x5916;&amp;#x90E8;&amp;#x7684;&amp;#x4EE3;&amp;#x7801;&amp;#x5F15;&amp;#x8D77;&amp;#x7684;\n&amp;#x4E0D;&amp;#x7A33;&amp;#x5B9A;&amp;#x7684;&amp;#x4F9D;&amp;#x8D56;&amp;#x5173;&amp;#x7CFB;, &amp;#x5982;HTTP Request, timer, slow animation, &amp;#x9700;&amp;#x8981;&amp;#x88AB;stubbed\n\n
  • &amp;#x5FC5;&amp;#x987B;&amp;#x548C;&amp;#x5916;&amp;#x90E8;&amp;#x4F9D;&amp;#x8D56;&amp;#x5173;&amp;#x7CFB;&amp;#x4E0D;&amp;#x76F8;&amp;#x5173;, &amp;#x4FDD;&amp;#x6301;&amp;#x5355;&amp;#x5143;&amp;#x7684;&amp;#x72EC;&amp;#x7ACB;&amp;#x6027;, &amp;#x662F;&amp;#x4E3A;&amp;#x4E86;&amp;#x4FDD;&amp;#x8BC1;&amp;#x9519;&amp;#x8BEF;&amp;#x4E0D;&amp;#x662F;&amp;#x7531;&amp;#x5916;&amp;#x90E8;&amp;#x7684;&amp;#x4EE3;&amp;#x7801;&amp;#x5F15;&amp;#x8D77;&amp;#x7684;\n&amp;#x4E0D;&amp;#x7A33;&amp;#x5B9A;&amp;#x7684;&amp;#x4F9D;&amp;#x8D56;&amp;#x5173;&amp;#x7CFB;, &amp;#x5982;HTTP Request, timer, slow animation, &amp;#x9700;&amp;#x8981;&amp;#x88AB;stubbed\n\n
  • &amp;#x5FC5;&amp;#x987B;&amp;#x548C;&amp;#x5916;&amp;#x90E8;&amp;#x4F9D;&amp;#x8D56;&amp;#x5173;&amp;#x7CFB;&amp;#x4E0D;&amp;#x76F8;&amp;#x5173;, &amp;#x4FDD;&amp;#x6301;&amp;#x5355;&amp;#x5143;&amp;#x7684;&amp;#x72EC;&amp;#x7ACB;&amp;#x6027;, &amp;#x662F;&amp;#x4E3A;&amp;#x4E86;&amp;#x4FDD;&amp;#x8BC1;&amp;#x9519;&amp;#x8BEF;&amp;#x4E0D;&amp;#x662F;&amp;#x7531;&amp;#x5916;&amp;#x90E8;&amp;#x7684;&amp;#x4EE3;&amp;#x7801;&amp;#x5F15;&amp;#x8D77;&amp;#x7684;\n&amp;#x4E0D;&amp;#x7A33;&amp;#x5B9A;&amp;#x7684;&amp;#x4F9D;&amp;#x8D56;&amp;#x5173;&amp;#x7CFB;, &amp;#x5982;HTTP Request, timer, slow animation, &amp;#x9700;&amp;#x8981;&amp;#x88AB;stubbed\n\n
  • &amp;#x5FC5;&amp;#x987B;&amp;#x548C;&amp;#x5916;&amp;#x90E8;&amp;#x4F9D;&amp;#x8D56;&amp;#x5173;&amp;#x7CFB;&amp;#x4E0D;&amp;#x76F8;&amp;#x5173;, &amp;#x4FDD;&amp;#x6301;&amp;#x5355;&amp;#x5143;&amp;#x7684;&amp;#x72EC;&amp;#x7ACB;&amp;#x6027;, &amp;#x662F;&amp;#x4E3A;&amp;#x4E86;&amp;#x4FDD;&amp;#x8BC1;&amp;#x9519;&amp;#x8BEF;&amp;#x4E0D;&amp;#x662F;&amp;#x7531;&amp;#x5916;&amp;#x90E8;&amp;#x7684;&amp;#x4EE3;&amp;#x7801;&amp;#x5F15;&amp;#x8D77;&amp;#x7684;\n&amp;#x4E0D;&amp;#x7A33;&amp;#x5B9A;&amp;#x7684;&amp;#x4F9D;&amp;#x8D56;&amp;#x5173;&amp;#x7CFB;, &amp;#x5982;HTTP Request, timer, slow animation, &amp;#x9700;&amp;#x8981;&amp;#x88AB;stubbed\n\n
  • &amp;#x5FC5;&amp;#x987B;&amp;#x548C;&amp;#x5916;&amp;#x90E8;&amp;#x4F9D;&amp;#x8D56;&amp;#x5173;&amp;#x7CFB;&amp;#x4E0D;&amp;#x76F8;&amp;#x5173;, &amp;#x4FDD;&amp;#x6301;&amp;#x5355;&amp;#x5143;&amp;#x7684;&amp;#x72EC;&amp;#x7ACB;&amp;#x6027;, &amp;#x662F;&amp;#x4E3A;&amp;#x4E86;&amp;#x4FDD;&amp;#x8BC1;&amp;#x9519;&amp;#x8BEF;&amp;#x4E0D;&amp;#x662F;&amp;#x7531;&amp;#x5916;&amp;#x90E8;&amp;#x7684;&amp;#x4EE3;&amp;#x7801;&amp;#x5F15;&amp;#x8D77;&amp;#x7684;\n&amp;#x4E0D;&amp;#x7A33;&amp;#x5B9A;&amp;#x7684;&amp;#x4F9D;&amp;#x8D56;&amp;#x5173;&amp;#x7CFB;, &amp;#x5982;HTTP Request, timer, slow animation, &amp;#x9700;&amp;#x8981;&amp;#x88AB;stubbed\n\n
  • &amp;#x5355;&amp;#x5143;&amp;#x6D4B;&amp;#x8BD5;&amp;#x53C8;&amp;#x79F0;&amp;#x6A21;&amp;#x5757;&amp;#x6D4B;&amp;#x8BD5;\n
  • &amp;#x5355;&amp;#x5143;&amp;#x6D4B;&amp;#x8BD5;&amp;#x53C8;&amp;#x79F0;&amp;#x6A21;&amp;#x5757;&amp;#x6D4B;&amp;#x8BD5;\n
  • &amp;#x5355;&amp;#x5143;&amp;#x6D4B;&amp;#x8BD5;&amp;#x53C8;&amp;#x79F0;&amp;#x6A21;&amp;#x5757;&amp;#x6D4B;&amp;#x8BD5;\n
  • &amp;#x5355;&amp;#x5143;&amp;#x6D4B;&amp;#x8BD5;&amp;#x53C8;&amp;#x79F0;&amp;#x6A21;&amp;#x5757;&amp;#x6D4B;&amp;#x8BD5;\n
  • &amp;#x5355;&amp;#x5143;&amp;#x6D4B;&amp;#x8BD5;&amp;#x53C8;&amp;#x79F0;&amp;#x6A21;&amp;#x5757;&amp;#x6D4B;&amp;#x8BD5;\n
  • &amp;#x8FD9;&amp;#x91CC;&amp;#x5E76;&amp;#x4E0D;&amp;#x662F;&amp;#x8BF4;&amp;#x6211;&amp;#x4EEC;&amp;#x4E0D;&amp;#x9700;&amp;#x8981;&amp;#x5199;functional testing. Functional Testing&amp;#x662F;&amp;#x4E00;&amp;#x79CD;&amp;#x8865;&amp;#x5145;, &amp;#x548C;unit testing&amp;#x4E00;&amp;#x6837;&amp;#x5FC5;&amp;#x4E0D;&amp;#x53EF;&amp;#x7F3A;\n&amp;#x6211;&amp;#x4EEC;&amp;#x8FD9;&amp;#x91CC;&amp;#x53EA;focus unit testing\n
  • &amp;#x8FD9;&amp;#x91CC;&amp;#x5E76;&amp;#x4E0D;&amp;#x662F;&amp;#x8BF4;&amp;#x6211;&amp;#x4EEC;&amp;#x4E0D;&amp;#x9700;&amp;#x8981;&amp;#x5199;functional testing. Functional Testing&amp;#x662F;&amp;#x4E00;&amp;#x79CD;&amp;#x8865;&amp;#x5145;, &amp;#x548C;unit testing&amp;#x4E00;&amp;#x6837;&amp;#x5FC5;&amp;#x4E0D;&amp;#x53EF;&amp;#x7F3A;\n&amp;#x6211;&amp;#x4EEC;&amp;#x8FD9;&amp;#x91CC;&amp;#x53EA;focus unit testing\n
  • &amp;#x8FD9;&amp;#x91CC;&amp;#x5E76;&amp;#x4E0D;&amp;#x662F;&amp;#x8BF4;&amp;#x6211;&amp;#x4EEC;&amp;#x4E0D;&amp;#x9700;&amp;#x8981;&amp;#x5199;functional testing. Functional Testing&amp;#x662F;&amp;#x4E00;&amp;#x79CD;&amp;#x8865;&amp;#x5145;, &amp;#x548C;unit testing&amp;#x4E00;&amp;#x6837;&amp;#x5FC5;&amp;#x4E0D;&amp;#x53EF;&amp;#x7F3A;\n&amp;#x6211;&amp;#x4EEC;&amp;#x8FD9;&amp;#x91CC;&amp;#x53EA;focus unit testing\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • &amp;#x9762;&amp;#x5411;&amp;#x51FD;&amp;#x6570;, &amp;#x5173;&amp;#x6CE8;&amp;#x4EE3;&amp;#x7801;&amp;#x5B9E;&amp;#x73B0;, &amp;#x6BD4;&amp;#x8F83;&amp;#x5728;&amp;#x610F;&amp;#x4EE3;&amp;#x7801;&amp;#x8986;&amp;#x76D6;&amp;#x7387; / &amp;#x9762;&amp;#x5411;&amp;#x529F;&amp;#x80FD;, &amp;#x5173;&amp;#x6CE8;&amp;#x4E1A;&amp;#x52A1;, &amp;#x7531;&amp;#x5916;&amp;#x53CA;&amp;#x5185;\n&amp;#x8F83;&amp;#x4E13;&amp;#x4E1A;&amp;#x5316;&amp;#x7684;&amp;#x6D4B;&amp;#x8BD5;&amp;#x8BED;&amp;#x53E5; / &amp;#x7B26;&amp;#x5408;&amp;#x8BED;&amp;#x8A00;&amp;#x4E60;&amp;#x60EF;, &amp;#x63A5;&amp;#x8FD1;&amp;#x81EA;&amp;#x7136;&amp;#x8BED;&amp;#x8A00;\n
  • &amp;#x9762;&amp;#x5411;&amp;#x51FD;&amp;#x6570;, &amp;#x5173;&amp;#x6CE8;&amp;#x4EE3;&amp;#x7801;&amp;#x5B9E;&amp;#x73B0;, &amp;#x6BD4;&amp;#x8F83;&amp;#x5728;&amp;#x610F;&amp;#x4EE3;&amp;#x7801;&amp;#x8986;&amp;#x76D6;&amp;#x7387; / &amp;#x9762;&amp;#x5411;&amp;#x529F;&amp;#x80FD;, &amp;#x5173;&amp;#x6CE8;&amp;#x4E1A;&amp;#x52A1;, &amp;#x7531;&amp;#x5916;&amp;#x53CA;&amp;#x5185;\n&amp;#x8F83;&amp;#x4E13;&amp;#x4E1A;&amp;#x5316;&amp;#x7684;&amp;#x6D4B;&amp;#x8BD5;&amp;#x8BED;&amp;#x53E5; / &amp;#x7B26;&amp;#x5408;&amp;#x8BED;&amp;#x8A00;&amp;#x4E60;&amp;#x60EF;, &amp;#x63A5;&amp;#x8FD1;&amp;#x81EA;&amp;#x7136;&amp;#x8BED;&amp;#x8A00;\n
  • &amp;#x9762;&amp;#x5411;&amp;#x51FD;&amp;#x6570;, &amp;#x5173;&amp;#x6CE8;&amp;#x4EE3;&amp;#x7801;&amp;#x5B9E;&amp;#x73B0;, &amp;#x6BD4;&amp;#x8F83;&amp;#x5728;&amp;#x610F;&amp;#x4EE3;&amp;#x7801;&amp;#x8986;&amp;#x76D6;&amp;#x7387; / &amp;#x9762;&amp;#x5411;&amp;#x529F;&amp;#x80FD;, &amp;#x5173;&amp;#x6CE8;&amp;#x4E1A;&amp;#x52A1;, &amp;#x7531;&amp;#x5916;&amp;#x53CA;&amp;#x5185;\n&amp;#x8F83;&amp;#x4E13;&amp;#x4E1A;&amp;#x5316;&amp;#x7684;&amp;#x6D4B;&amp;#x8BD5;&amp;#x8BED;&amp;#x53E5; / &amp;#x7B26;&amp;#x5408;&amp;#x8BED;&amp;#x8A00;&amp;#x4E60;&amp;#x60EF;, &amp;#x63A5;&amp;#x8FD1;&amp;#x81EA;&amp;#x7136;&amp;#x8BED;&amp;#x8A00;\n
  • &amp;#x9762;&amp;#x5411;&amp;#x51FD;&amp;#x6570;, &amp;#x5173;&amp;#x6CE8;&amp;#x4EE3;&amp;#x7801;&amp;#x5B9E;&amp;#x73B0;, &amp;#x6BD4;&amp;#x8F83;&amp;#x5728;&amp;#x610F;&amp;#x4EE3;&amp;#x7801;&amp;#x8986;&amp;#x76D6;&amp;#x7387; / &amp;#x9762;&amp;#x5411;&amp;#x529F;&amp;#x80FD;, &amp;#x5173;&amp;#x6CE8;&amp;#x4E1A;&amp;#x52A1;, &amp;#x7531;&amp;#x5916;&amp;#x53CA;&amp;#x5185;\n&amp;#x8F83;&amp;#x4E13;&amp;#x4E1A;&amp;#x5316;&amp;#x7684;&amp;#x6D4B;&amp;#x8BD5;&amp;#x8BED;&amp;#x53E5; / &amp;#x7B26;&amp;#x5408;&amp;#x8BED;&amp;#x8A00;&amp;#x4E60;&amp;#x60EF;, &amp;#x63A5;&amp;#x8FD1;&amp;#x81EA;&amp;#x7136;&amp;#x8BED;&amp;#x8A00;\n
  • &amp;#x9762;&amp;#x5411;&amp;#x51FD;&amp;#x6570;, &amp;#x5173;&amp;#x6CE8;&amp;#x4EE3;&amp;#x7801;&amp;#x5B9E;&amp;#x73B0;, &amp;#x6BD4;&amp;#x8F83;&amp;#x5728;&amp;#x610F;&amp;#x4EE3;&amp;#x7801;&amp;#x8986;&amp;#x76D6;&amp;#x7387; / &amp;#x9762;&amp;#x5411;&amp;#x529F;&amp;#x80FD;, &amp;#x5173;&amp;#x6CE8;&amp;#x4E1A;&amp;#x52A1;, &amp;#x7531;&amp;#x5916;&amp;#x53CA;&amp;#x5185;\n&amp;#x8F83;&amp;#x4E13;&amp;#x4E1A;&amp;#x5316;&amp;#x7684;&amp;#x6D4B;&amp;#x8BD5;&amp;#x8BED;&amp;#x53E5; / &amp;#x7B26;&amp;#x5408;&amp;#x8BED;&amp;#x8A00;&amp;#x4E60;&amp;#x60EF;, &amp;#x63A5;&amp;#x8FD1;&amp;#x81EA;&amp;#x7136;&amp;#x8BED;&amp;#x8A00;\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • Jasmine&amp;#x81EA;&amp;#x5E26;&amp;#x7684;Spy&amp;#x884C;&amp;#x4E3A;&amp;#x6709;&amp;#x4E9B;&amp;#x5947;&amp;#x602A;, spy&amp;#x5E94;&amp;#x8BE5;&amp;#x8BBE;&amp;#x8BA1;&amp;#x4E3A;&amp;#x4E0D;&amp;#x963B;&amp;#x6B62;&amp;#x539F;&amp;#x59CB;&amp;#x7684;&amp;#x884C;&amp;#x4E3A;\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • stub&amp;#x548C;spy&amp;#x7684;&amp;#x6700;&amp;#x5927;&amp;#x533A;&amp;#x522B;&amp;#x662F;stub&amp;#x540E;&amp;#x7684;&amp;#x65B9;&amp;#x6CD5;, &amp;#x539F;&amp;#x59CB;&amp;#x884C;&amp;#x4E3A;&amp;#x662F;&amp;#x4E0D;&amp;#x4F1A;&amp;#x88AB;&amp;#x6267;&amp;#x884C;&amp;#x7684;.\n
  • \n
  • \n
  • stub&amp;#x53EF;&amp;#x4EE5;&amp;#x505A;&amp;#x66F4;&amp;#x591A;, &amp;#x4E0D;&amp;#x4EC5;&amp;#x4EC5;&amp;#x662F;&amp;#x8FD4;&amp;#x56DE;&amp;#x503C;, &amp;#x63A7;&amp;#x5236;&amp;#x56DE;&amp;#x8C03;&amp;#x51FD;&amp;#x6570;&amp;#x7684;&amp;#x53C2;&amp;#x6570;&amp;#x4E5F;&amp;#x662F;&amp;#x53EF;&amp;#x884C;&amp;#x7684;.\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • Transcript of "Client Side Unit Testing"

    1. 1. Client-Side Unit Testing Cloud Chen 2012/5/11
    2. 2. You don’t write tests
    3. 3. You know you should, but you don’t
    4. 4. You won’t be blamed for it
    5. 5. Because...
    6. 6. There are many common issues that prevent developers for writing tests
    7. 7. You think
    8. 8. You think• Tests are not necessary and irrelevant
    9. 9. You think• Tests are not necessary and irrelevant• Manual testing is enough
    10. 10. You think• Tests are not necessary and irrelevant• Manual testing is enough• Lacking of specification
    11. 11. You think• Tests are not necessary and irrelevant• Manual testing is enough• Lacking of specification• Spaghetti code are hard for testing
    12. 12. You think• Tests are not necessary and irrelevant• Manual testing is enough• Lacking of specification• Spaghetti code are hard for testing• Web application needs functional testing rather than unit testing
    13. 13. You think• Tests are not necessary and irrelevant• Manual testing is enough• Lacking of specification• Spaghetti code are hard for testing• Web application needs functional testing rather than unit testing• Unit testing looks like only useful for back-end code rather than front-end
    14. 14. You think• Tests are not necessary and irrelevant• Manual testing is enough• Lacking of specification• Spaghetti code are hard for testing• Web application needs functional testing rather than unit testing• Unit testing looks like only useful for back-end code rather than front-end• Lazy...
    15. 15. Our goal
    16. 16. Our goal Help you start writing unit testsfor front-end code
    17. 17. However,
    18. 18. Let’s start with another type of testing
    19. 19. Functional Testing
    20. 20. What is functional testing Functional testing is a type of black box testing that bases its test cases on the specifications of the software component under test. Functions are tested by feeding them input and examining the output, and internal program structure is rarely considered.
    21. 21. What is functional testing Functional testing is a type of black box testing that bases its test cases on the specifications of the software component under test. Functions are tested by feeding them input and examining the output, and internal program structure is rarely considered.
    22. 22. In a nutshell
    23. 23. In a nutshell• Written from user perspective
    24. 24. In a nutshell• Written from user perspective• Proving users are able to reproduce defined steps
    25. 25. In a nutshell• Written from user perspective• Proving users are able to reproduce defined steps• Don’t need to consider internal program structure
    26. 26. In a nutshell• Written from user perspective• Proving users are able to reproduce defined steps• Don’t need to consider internal program structure• Automating manual testing
    27. 27. Unit Testing
    28. 28. What is unit testingunit testing is a method by which individualunits of source code, sets of one or morecomputer program modules together withassociated control data, usage procedures, andoperating procedures, are tested to determineif they are fit for use.
    29. 29. What is unit testingunit testing is a method by which individualunits of source code, sets of one or morecomputer program modules together withassociated control data, usage procedures, andoperating procedures, are tested to determineif they are fit for use.
    30. 30. In a nutshell
    31. 31. In a nutshell• Unit testing is complete isolation
    32. 32. In a nutshell• Unit testing is complete isolation• Must irrelevant to external dependencies
    33. 33. In a nutshell• Unit testing is complete isolation• Must irrelevant to external dependencies• All unreliable or slow dependencies of a tested unit should be stubbed
    34. 34. In a nutshell• Unit testing is complete isolation• Must irrelevant to external dependencies• All unreliable or slow dependencies of a tested unit should be stubbed• Only the logic of that single unit is exercised
    35. 35. In a nutshell• Unit testing is complete isolation• Must irrelevant to external dependencies• All unreliable or slow dependencies of a tested unit should be stubbed• Only the logic of that single unit is exercised• Must be fast
    36. 36. What are differences between functional and unit testing
    37. 37. What are differences between functional and unit testing unit functionalperspective programmer user every module every process goal works as expected works as expected all cases coverage most user cases even edge cases result programmer :) user :)
    38. 38. What are differences between functional and unit testing unit functionalperspective programmer user every module every process goal works as expected works as expected all cases coverage most user cases even edge cases result programmer :) user :)
    39. 39. What are differences between functional and unit testing unit functionalperspective programmer user every module every process goal works as expected works as expected all cases coverage most user cases even edge cases result programmer :) user :)
    40. 40. What are differences between functional and unit testing unit functionalperspective programmer user every module every process goal works as expected works as expected all cases coverage most user cases even edge cases result programmer :) user :)
    41. 41. What are differences between functional and unit testing unit functionalperspective programmer user every module every process goal works as expected works as expected all cases coverage most user cases even edge cases result programmer :) user :)
    42. 42. Hence..
    43. 43. Hence..• We are programmer
    44. 44. Hence..• We are programmer• We care our code quality
    45. 45. Hence..• We are programmer• We care our code quality• We should write unit test code
    46. 46. Why, When ofunit testing
    47. 47. Why need unit testing
    48. 48. Why need unit testing• Ensuring every component is bug-free
    49. 49. Why need unit testing• Ensuring every component is bug-free• Ensuring every component is easy to modify
    50. 50. Why need unit testing• Ensuring every component is bug-free• Ensuring every component is easy to modify• Easy to locating bug of complex logic
    51. 51. Why need unit testing• Ensuring every component is bug-free• Ensuring every component is easy to modify• Easy to locating bug of complex logic• Preventing and capturing regression bug
    52. 52. Why need unit testing• Ensuring every component is bug-free• Ensuring every component is easy to modify• Easy to locating bug of complex logic• Preventing and capturing regression bug• Testable code must be high readability and maintainability code
    53. 53. Why need unit testing• Ensuring every component is bug-free• Ensuring every component is easy to modify• Easy to locating bug of complex logic• Preventing and capturing regression bug• Testable code must be high readability and maintainability code• and so on...
    54. 54. When to do unit testing
    55. 55. When to do unit testing• When you write your own classes, modules, libraries, frameworks.
    56. 56. When to do unit testing• When you write your own classes, modules, libraries, frameworks.• You don’t need to write test code for fundamental framework
    57. 57. testing style comparison
    58. 58. testing style comparison TDD BDD based on function oriented feature oriented syntax testing language idiomaticspring from programmer stakeholder || POaccumulation test as code test as documentation
    59. 59. testing style comparison TDD BDD based on function oriented feature oriented syntax testing language idiomaticspring from programmer stakeholder || POaccumulation test as code test as documentation
    60. 60. testing style comparison TDD BDD based on function oriented feature oriented syntax testing language idiomaticspring from programmer stakeholder || POaccumulation test as code test as documentation
    61. 61. testing style comparison TDD BDD based on function oriented feature oriented syntax testing language idiomaticspring from programmer stakeholder || POaccumulation test as code test as documentation
    62. 62. testing style comparison TDD BDD based on function oriented feature oriented syntax testing language idiomaticspring from programmer stakeholder || POaccumulation test as code test as documentation
    63. 63. testing style comparison
    64. 64. testing style comparison• It doesn’t matter which style you choose
    65. 65. testing style comparison• It doesn’t matter which style you choose• It does matter how many cases you have
    66. 66. testing style comparison
    67. 67. testing style comparison As we are using Scrum,
    68. 68. testing style comparison As we are using Scrum, BDD is more suitable for us.
    69. 69. BDD Framework
    70. 70. BDD Framework• Focuses on assertion, doesn’t depend on DOM
    71. 71. BDD Framework• Focuses on assertion, doesn’t depend on DOM• Succinct API
    72. 72. BDD Framework• Focuses on assertion, doesn’t depend on DOM• Succinct API• Natively support Spy
    73. 73. BDD Framework• Focuses on assertion, doesn’t depend on DOM• Succinct API• Natively support Spy• Support spec helper
    74. 74. BDD Framework• Focuses on assertion, doesn’t depend on DOM• Succinct API• Natively support Spy• Support spec helper• Be able to extend matcher
    75. 75. BDD Framework• Focuses on assertion, doesn’t depend on DOM• Succinct API• Natively support Spy• Support spec helper• Be able to extend matcher• Be able to run in several environments
    76. 76. BDD Framework• Focuses on assertion, doesn’t depend on DOM• Succinct API• Natively support Spy• Support spec helper• Be able to extend matcher• Be able to run in several environments 1. Browser
    77. 77. BDD Framework• Focuses on assertion, doesn’t depend on DOM• Succinct API• Natively support Spy• Support spec helper• Be able to extend matcher• Be able to run in several environments 1. Browser 2. CI
    78. 78. BDD Framework• Focuses on assertion, doesn’t depend on DOM• Succinct API• Natively support Spy• Support spec helper• Be able to extend matcher• Be able to run in several environments 1. Browser 2. CI 3. NodeJS
    79. 79. Typical Spec & Succinct APIdescribe("Flashcard model", function () { beforeEach(function () { this.Flashcard = require(models/Flashcard); }); afterEach(function () { }); describe(“when initialized”, function() { beforeEach(function() { this.flashcard = new this.Flashcard({ "Value": "here", "Translation": "这儿", "Audio": "here_en.mp3", "IsWord": true, "ContentId": 174087, "IsWordIKnow": false }); }); it(“composite key should be composed by ContentId and IsWord properties”,function() { var expected_url = this.flashcard.get(ContentId) + - +this.flashcard.get(IsWord); expect(this.flashcard.id).toEqual(expected_url); }); });
    80. 80. Typical Spec & Succinct APIdescribe("Flashcard model", function () { beforeEach(function () { this.Flashcard = require(models/Flashcard); Suite }); afterEach(function () { }); describe(“when initialized”, function() { beforeEach(function() { this.flashcard = new this.Flashcard({ "Value": "here", "Translation": "这儿", "Audio": "here_en.mp3", "IsWord": true, "ContentId": 174087, "IsWordIKnow": false }); }); it(“composite key should be composed by ContentId and IsWord properties”,function() { var expected_url = this.flashcard.get(ContentId) + - +this.flashcard.get(IsWord); expect(this.flashcard.id).toEqual(expected_url); }); });
    81. 81. Typical Spec & Succinct APIdescribe("Flashcard model", function () { beforeEach(function () { this.Flashcard = require(models/Flashcard); }); afterEach(function () { Inner Suite }); describe(“when initialized”, function() { beforeEach(function() { this.flashcard = new this.Flashcard({ "Value": "here", "Translation": "这儿", "Audio": "here_en.mp3", "IsWord": true, "ContentId": 174087, "IsWordIKnow": false }); }); it(“composite key should be composed by ContentId and IsWord properties”,function() { var expected_url = this.flashcard.get(ContentId) + - +this.flashcard.get(IsWord); expect(this.flashcard.id).toEqual(expected_url); }); });
    82. 82. Typical Spec & Succinct APIdescribe("Flashcard model", function () { beforeEach(function () { this.Flashcard = require(models/Flashcard); }); afterEach(function () { }); describe(“when initialized”, function() { beforeEach(function() { this.flashcard = new this.Flashcard({ "Value": "here", "Translation": "这儿", "Audio": "here_en.mp3", "IsWord": true, "ContentId": 174087, }); "IsWordIKnow": false Spec }); it(“composite key should be composed by ContentId and IsWord properties”,function() { var expected_url = this.flashcard.get(ContentId) + - +this.flashcard.get(IsWord); expect(this.flashcard.id).toEqual(expected_url); }); });
    83. 83. Typical Spec & Succinct APIdescribe("Flashcard model", function () { beforeEach(function () { this.Flashcard = require(models/Flashcard); }); afterEach(function () { }); describe(“when initialized”, function() { beforeEach(function() { this.flashcard = new this.Flashcard({ "Value": "here", "Translation": "这儿", "Audio": "here_en.mp3", "IsWord": true, "ContentId": 174087, "IsWordIKnow": false }); }); it(“composite key should be composed by ContentId and IsWord properties”,function() { var expected_url = this.flashcard.get(ContentId) + - +this.flashcard.get(IsWord); expect(this.flashcard.id).toEqual(expected_url); Expectation }); });
    84. 84. Typical Spec & Succinct APIdescribe("Flashcard model", function () { beforeEach(function () { this.Flashcard = require(models/Flashcard); }); afterEach(function () { }); describe(“when initialized”, function() { beforeEach(function() { this.flashcard = new this.Flashcard({ "Value": "here", "Translation": "这儿", "Audio": "here_en.mp3", "IsWord": true, "ContentId": 174087, "IsWordIKnow": false }); }); it(“composite key should be composed by ContentId and IsWord properties”,function() { var expected_url = this.flashcard.get(ContentId) + - +this.flashcard.get(IsWord); expect(this.flashcard.id).toEqual(expected_url); }); }); Matcher
    85. 85. Typical Spec & Succinct APIdescribe("Flashcard model", function () { beforeEach(function () { this.Flashcard = require(models/Flashcard); Setup }); afterEach(function () { }); describe(“when initialized”, function() { beforeEach(function() { this.flashcard = new this.Flashcard({ "Value": "here", "Translation": "这儿", "Audio": "here_en.mp3", "IsWord": true, "ContentId": 174087, "IsWordIKnow": false }); }); it(“composite key should be composed by ContentId and IsWord properties”,function() { var expected_url = this.flashcard.get(ContentId) + - +this.flashcard.get(IsWord); expect(this.flashcard.id).toEqual(expected_url); }); });
    86. 86. Typical Spec & Succinct APIdescribe("Flashcard model", function () { beforeEach(function () { this.Flashcard = require(models/Flashcard); Setup }); afterEach(function () { }); Tear down describe(“when initialized”, function() { beforeEach(function() { this.flashcard = new this.Flashcard({ "Value": "here", "Translation": "这儿", "Audio": "here_en.mp3", "IsWord": true, "ContentId": 174087, "IsWordIKnow": false }); }); it(“composite key should be composed by ContentId and IsWord properties”,function() { var expected_url = this.flashcard.get(ContentId) + - +this.flashcard.get(IsWord); expect(this.flashcard.id).toEqual(expected_url); }); });
    87. 87. Other Native Matchersexpect(x).toEqual(y);expect(x).toBe(y);expect(x).toMatch(pattern);expect(x).toBeDefined();expect(x).toBeUndefined();expect(x).toBeNull();expect(x).toBeTruthy();expect(x).toBeFalsy();expect(x).toContain(y);expect(x).toBeLessThan(y);expect(x).toBeGreaterThan(y);expect(function(){fn();}).toThrow(e);
    88. 88. Other Native Matchersexpect(x).not.toEqual(y);expect(x).not.toBe(y);expect(x).not.toMatch(pattern);expect(x).not.toBeDefined();expect(x).not.toBeUndefined();expect(x).not.toBeNull();expect(x).not.toBeTruthy();expect(x).not.toBeFalsy();expect(x).not.toContain(y);expect(x).not.toBeLessThan(y);expect(x).not.toBeGreaterThan(y);expect(function(){fn();}).not.toThrow(e);
    89. 89. Spec HelperbeforeEach(function() { this.fixtures = { Flashcard: { valid: { // response starts here "Value": "here", "Translation": "这儿", "Audio": "here_en.mp3", "IsWord": true, "ContentId": 174087, "IsWordIKnow": false }, error: { // response starts here "ErrorCode": "101", "IsSuccess": "false" } } }
    90. 90. Spec HelperbeforeEach(function() { this.fixtures = { Flashcard: { valid: { // response starts here "Value": "here", "Translation": "这儿", "Audio": "here_en.mp3", "IsWord": true, "ContentId": 174087, "IsWordIKnow": false }, error: { // response starts here "ErrorCode": "101", "IsSuccess": "false" } } } Using this.fixtures.Flashcard.valid to access pre-defined fixture for testing when this spec file is included in your spec runner.
    91. 91. Spydescribe("Flashcard model", function () { beforeEach(function () { this.Flashcard = require(models/Flashcard); }); describe(“when initialized”, function() { beforeEach(function() { spyOn(jQuery, ‘ajax’); this.flashcard = new this.Flashcard(); this.flashcard.fetch(); }); it(“should communicate with back-end via http get method”, function() { expect(jQuery.ajax).toHaveBeenCalled(); expect(jQuery.ajax.mostRecentCall.args[0].type).toEqual(‘GET’); }); });});
    92. 92. Spydescribe("Flashcard model", function () { beforeEach(function () { this.Flashcard = require(models/Flashcard); }); describe(“when initialized”, function() { beforeEach(function() { spyOn(jQuery, ‘ajax’); spy on instance method this.flashcard = new this.Flashcard(); this.flashcard.fetch(); }); it(“should communicate with back-end via http get method”, function() { expect(jQuery.ajax).toHaveBeenCalled(); expect(jQuery.ajax.mostRecentCall.args[0].type).toEqual(‘GET’); }); });});
    93. 93. Spydescribe("Flashcard model", function () { beforeEach(function () { this.Flashcard = require(models/Flashcard); }); describe(“when initialized”, function() { beforeEach(function() { spyOn(jQuery, ‘ajax’); this.flashcard = new this.Flashcard(); this.flashcard.fetch(); }); original function won’t be called it(“should communicate with back-end via http get method”, function() { expect(jQuery.ajax).toHaveBeenCalled(); expect(jQuery.ajax.mostRecentCall.args[0].type).toEqual(‘GET’); }); });});
    94. 94. Spydescribe("Flashcard model", function () { beforeEach(function () { this.Flashcard = require(models/Flashcard); }); describe(“when initialized”, function() { beforeEach(function() { spyOn(jQuery, ‘ajax’); this.flashcard = new this.Flashcard(); this.flashcard.fetch(); }); it(“should communicate with back-end via http get method”, function() { expect(jQuery.ajax).toHaveBeenCalled(); expect(jQuery.ajax.mostRecentCall.args[0].type).toEqual(‘GET’); }); }); match spied function was called});
    95. 95. Spydescribe("Flashcard model", function () { beforeEach(function () { this.Flashcard = require(models/Flashcard); }); describe(“when initialized”, function() { beforeEach(function() { spyOn(jQuery, ‘ajax’); this.flashcard = new this.Flashcard(); this.flashcard.fetch(); }); it(“should communicate with back-end via http get method”, function() { expect(jQuery.ajax).toHaveBeenCalled(); expect(jQuery.ajax.mostRecentCall.args[0].type).toEqual(‘GET’); }); });}); arguments of last call
    96. 96. Spydescribe("Flashcard model", function () { beforeEach(function () { this.Flashcard = require(models/Flashcard); }); describe(“when initialized”, function() { beforeEach(function() { spyOn(jQuery, ‘ajax’); this.flashcard = new this.Flashcard(); this.flashcard.fetch(); }); it(“should communicate with back-end via http get method”, function() { expect(jQuery.ajax).toHaveBeenCalled(); expect(jQuery.ajax.mostRecentCall.args[0].type).toEqual(‘GET’); }); });}); arguments of last call type of first argument
    97. 97. Extending Matcher<a href=”#” class=”link”>this is a link</a>expect($(‘a’).hasClass(s-selected)).toBeTruthy(); expectation failed: Expected false to be truthy.
    98. 98. Extending Matcher<a href=”#” class=”link”>this is a link</a>expect($(‘a’).hasClass(s-selected)).toBeTruthy(); expectation failed: Expected false to be truthy. jasmine-jquery comes to rescue
    99. 99. Matcher from jasmine jquery<a href=”#” class=”link”>this is a link</a>expect($(‘a’).hasClass(s-selected)).toBeTruthy(); expectation failed: Expected false to be truthy.
    100. 100. Matcher from jasmine jquery<a href=”#” class=”link”>this is a link</a>expect($(‘a’).hasClass(s-selected)).toBeTruthy(); expectation failed: Expected false to be truthy.expect($(‘a’).toHaveClass(‘s-selected’);
    101. 101. Matcher from jasmine jquery<a href=”#” class=”link”>this is a link</a>expect($(‘a’).hasClass(s-selected)).toBeTruthy(); expectation failed: Expected false to be truthy.expect($(‘a’).toHaveClass(‘s-selected’); expectation failed:
    102. 102. Matcher from jasmine jquery<a href=”#” class=”link”>this is a link</a>expect($(‘a’).hasClass(s-selected)).toBeTruthy(); expectation failed: Expected false to be truthy.expect($(‘a’).toHaveClass(‘s-selected’); expectation failed: Expected <a></a> to have class s-selected.
    103. 103. jasmine-jquery features• a set of custom matchers for jQuery framework• an API for handling HTML fixtures in your specs
    104. 104. jasmine-jquery matchersexpect(x).toBeHidden()expect(x).toBeVisible()expect(x).toHaveAttr(attributeName, attributeValue)expect(x).toHaveProp(propertyName, propertyValue)expect(x).toHaveText(string)expect(x).toHaveHtml(string)expect(x).toHaveId(id)expect(x).toBeDisabled()expect(x).toBeFocused()expect(x).toHandle(eventName)
    105. 105. jasmine-jquery fixturesIn myfixture.html file:<div id="my-fixture">some complex content here</div>Inside your test:loadFixtures(myfixture.html);$(#my-fixture).myTestedPlugin();expect($(#my-fixture)).to...;
    106. 106. Running JasmineStandalone Runner * Manually manage of your project files and specs
    107. 107. Running JasmineDedicated Server runs jasmineRuby jasmine Gem * Using yaml manages of your project files and specs
    108. 108. Recap rules of Unit Testing• Unit testing is complete isolation• Must irrelevant to external dependencies• All unreliable or slow dependencies of a tested unit should be stubbed• Only the logic of that single unit is exercised• Must be fast
    109. 109. Recap rules of Unit Testing• Unit testing is complete isolation• Must irrelevant to external dependencies• All unreliable or slow dependencies of a tested unit should be stubbed• Only the logic of that single unit is exercised• Must be fast
    110. 110. Stub every dependency
    111. 111. Stub every dependency• Native spy feature of Jasmine is not enough
    112. 112. Stub every dependency• Native spy feature of Jasmine is not enough• It doesn’t support fake timer
    113. 113. Stub every dependency• Native spy feature of Jasmine is not enough• It doesn’t support fake timer• It doesn’t support fake HTTP server
    114. 114. Stub every dependency• Native spy feature of Jasmine is not enough• It doesn’t support fake timer• It doesn’t support fake HTTP server• It misuses Spy and Stub
    115. 115. Stub every dependency• Native spy feature of Jasmine is not enough• It doesn’t support fake timer• It doesn’t support fake HTTP server• It misuses Spy and Stub Sinon.js comes to rescue
    116. 116. Spy/Stub/Mock
    117. 117. Spy/Stub/Mock• A test spy is a function that records arguments, return value, the value of this and exception thrown (if any) for all its calls. A test spy can be an anonymous function or it can wrap an existing function.
    118. 118. Spy/Stub/Mock• A test spy is a function that records arguments, return value, the value of this and exception thrown (if any) for all its calls. A test spy can be an anonymous function or it can wrap an existing function.• Test stubs are functions (spies) with pre-programmed behavior. They support the full test spy API in addition to methods which can be used to alter the stubs behavior.
    119. 119. Spy/Stub/Mock• A test spy is a function that records arguments, return value, the value of this and exception thrown (if any) for all its calls. A test spy can be an anonymous function or it can wrap an existing function.• Test stubs are functions (spies) with pre-programmed behavior. They support the full test spy API in addition to methods which can be used to alter the stubs behavior.• Mocks (and mock expectations) are fake methods (like spies) with pre-programmed behavior (like stubs) as well as pre- programmed expectations. A mock will fail your test if it is not used as expected.
    120. 120. Spy/Stub/Mock
    121. 121. Spy/Stub/Mock Spy
    122. 122. Spy/Stub/Mock Spy Stub
    123. 123. Spy/Stub/Mock Spy Stub Mock
    124. 124. Spydescribe("when initialized", function () { beforeEach(function() { sinon.spy(this, "Flashcards"); create a spy for this.Flashcards }) afterEach(function() { this.Flashcards.restore(); }) it("should throw Error when cultureCode is not passed", function() { try { var flashcards = new this.Flashcards([], { }); } catch(e) {} expect(this.Flashcards.calledOnce()).toBeTruthy(); expect(this.Flashcards.threw()).toBeTruthy(); expect(this.Flashcards.calledWith([], {})).toBeTruthy(); });});
    125. 125. Spydescribe("when initialized", function () { beforeEach(function() { sinon.spy(this, "Flashcards"); }) afterEach(function() { this.Flashcards.restore(); unwraps the spy }) it("should throw Error when cultureCode is not passed", function() { try { var flashcards = new this.Flashcards([], { }); } catch(e) {} expect(this.Flashcards.calledOnce()).toBeTruthy(); expect(this.Flashcards.threw()).toBeTruthy(); expect(this.Flashcards.calledWith([], {})).toBeTruthy(); });});
    126. 126. Spydescribe("when initialized", function () { beforeEach(function() { sinon.spy(this, "Flashcards"); }) afterEach(function() { this.Flashcards.restore(); }) it("should throw Error when cultureCode is not passed", function() { try { var flashcards = new this.Flashcards([], { }); } catch(e) {} expect(this.Flashcards.calledOnce()).toBeTruthy(); spy was called once expect(this.Flashcards.threw()).toBeTruthy(); expect(this.Flashcards.calledWith([], {})).toBeTruthy(); });});
    127. 127. Spydescribe("when initialized", function () { beforeEach(function() { sinon.spy(this, "Flashcards"); }) afterEach(function() { this.Flashcards.restore(); }) it("should throw Error when cultureCode is not passed", function() { try { var flashcards = new this.Flashcards([], { }); } catch(e) {} expect(this.Flashcards.calledOnce()).toBeTruthy(); expect(this.Flashcards.threw()).toBeTruthy(); expect(this.Flashcards.calledWith([], {})).toBeTruthy(); });}); spy threw exception at least once
    128. 128. Spydescribe("when initialized", function () { beforeEach(function() { sinon.spy(this, "Flashcards"); }) afterEach(function() { this.Flashcards.restore(); }) it("should throw Error when cultureCode is not passed", function() { try { var flashcards = new this.Flashcards([], { }); } catch(e) {} expect(this.Flashcards.calledOnce()).toBeTruthy(); expect(this.Flashcards.threw()).toBeTruthy(); expect(this.Flashcards.calledWith([], {})).toBeTruthy(); });}); spy was called at least once with provided argument
    129. 129. Stubdescribe("when update model", function () { beforeEach(function () { this.sync = sinon.stub(Backbone, "sync"); }); afterEach(function () { create a stub for this.Flashcards this.sync.restore(); }); it("should put IsWordIKnow and IsWord properties at the end of url property",function () { this.flashcard.set(IsWordIKnow, true); var expected_url = this.flashcard.url() + &IsWordIKnow= +this.flashcard.get(IsWordIKnow) + &IsWord= + this.flashcard.get(IsWord); this.flashcard.save(); expect(this.sync.getCall(0).args[2].url).toEqual(expected_url); });});
    130. 130. Stubdescribe("when update model", function () { beforeEach(function () { this.sync = sinon.stub(Backbone, "sync"); }); afterEach(function () { this.sync.restore(); }); it("should put IsWordIKnow and IsWord properties at the end of url property",function () { this.flashcard.set(IsWordIKnow, true); var expected_url = this.flashcard.url() + &IsWordIKnow= +this.flashcard.get(IsWordIKnow) + &IsWord= + this.flashcard.get(IsWord); this.flashcard.save(); expect(this.sync.getCall(0).args[2].url).toEqual(expected_url); });}); save method will invoke Backbone.sync that already stubbed
    131. 131. Stubdescribe("when update model", function () { beforeEach(function () { this.sync = sinon.stub(Backbone, "sync"); }); afterEach(function () { this.sync.restore(); }); it("should put IsWordIKnow and IsWord properties at the end of url property",function () { this.flashcard.set(IsWordIKnow, true); var expected_url = this.flashcard.url() + &IsWordIKnow= +this.flashcard.get(IsWordIKnow) + &IsWord= + this.flashcard.get(IsWord); this.flashcard.save(); expect(this.sync.getCall(0).args[2].url).toEqual(expected_url); });}); original sync method won’t be called if it was stubbed So, no ajax request won’t be fired
    132. 132. Stubdescribe("when update model", function () { beforeEach(function () { this.sync = sinon.stub(Backbone, "sync"); }); afterEach(function () { this.sync.restore(); }); it("should put IsWordIKnow and IsWord properties at the end of url property",function () { this.flashcard.set(IsWordIKnow, true); var expected_url = this.flashcard.url() + &IsWordIKnow= +this.flashcard.get(IsWordIKnow) + &IsWord= + this.flashcard.get(IsWord); this.flashcard.save(); expect(this.sync.getCall(0).args[2].url).toEqual(expected_url); });}); get arguments of stubbed calling (spy can also do this)
    133. 133. Stub But, Spy cannot do like so:it("should always confirm every confirmation", function () { sinon.stub(window, confirm); confirm.returns(true); expect(confirm(Are you sure?)).toBeTruthy(); window.confirm.restore();} Makes window.confirm() return truth
    134. 134. Stub But, Spy cannot do like so:it("should always confirm every confirmation", function () { sinon.stub(window, confirm); confirm.returns(true); expect(confirm(Are you sure?)).toBeTruthy(); window.confirm.restore();} Native confirm behavior won’t fired
    135. 135. Stub But, Spy cannot do like so:it("should always confirm every confirmation", function () { sinon.stub(window, confirm); confirm.returns(true); expect(confirm(Are you sure?)).toBeTruthy(); window.confirm.restore();} Native confirm behavior won’t fired It needs user interaction to finish this test without stub. This test probably be failed if user not confirms with it. That test case is unstable.
    136. 136. Mock• Mock focuses on implementation details of one method• It utilizes upfront expectation to verify details rather than asserting after the details
    137. 137. Mockdescribe("when initialized", function () { it("should throw Error when cultureCode is not passed", function() { var myAPI = { method: function () {} }; var spy = sinon.spy(); var mock = sinon.mock(myAPI); mock.expects("method").once(); expectation upfront myAPI.method(); spy(); verify mock behavior mock.verify(); expect(spy.calledOnce).toBeTruthy(); });});
    138. 138. Without FakeTimersit("should show teacher box after 1 hour", function () { var hour = 1000 * 60 * 60; setTimeout(showTeacherbox, hour); waits(hour); must wait 1 hour...crazy runs(function() { expect($(‘#teacherbox’)).toBeVisible(); });});
    139. 139. FakeTimersit("should show teacher box after 1 hour", function () { this.clock = sinon.useFakeTimers(); var hour = 1000 * 60 * 60; create a fake timer setTimeout(showTeacherbox, hour); this.clock.tick(hour); expect($(‘#teacherbox’)).toBeVisible(); this.clock.restore();});
    140. 140. FakeTimersit("should show teacher box after 1 hour", function () { this.clock = sinon.useFakeTimers(); var hour = 1000 * 60 * 60; setTimeout(showTeacherbox, hour); tick the clock ahead 1 hour this.clock.tick(hour); expect($(‘#teacherbox’)).toBeVisible(); this.clock.restore();});
    141. 141. FakeTimersit("should show teacher box after 1 hour", function () { this.clock = sinon.useFakeTimers(); var hour = 1000 * 60 * 60; setTimeout(showTeacherbox, hour); this.clock.tick(hour - 1); expect($(‘#teacherbox’)).toBeHidden(); won’t happen this.clock.tick(1); expect($(‘#teacherbox’)).toBeVisible(); will happen this.clock.restore();});
    142. 142. Fake Serverdescribe("Flashcard model", function () { beforeEach(function () { this.Flashcard = require(models/Flashcard); }); describe(“when fetch”, function() { beforeEach(function() { this.server = sinon.fakeServer.create(); this.flashcard = new this.Flashcard({contentId: 174087}); }); afterEach(function() { this.server.restore(); }); it(“should get data from backend”, function() { this.server.respondWith( "GET", "/community/dailylesson/wordiknowupdate.ashx?contentId=174087", [200, { "Content-Type": "application/json" }, { "Value": "here", "Translation": "这儿", "Audio": "here_en.mp3", "IsWord": true, "ContentId": 174087, "IsWordIKnow": false } ] ); this.flashcard.fetch(); this.server.respond(); expect(this.flashcard.get(‘Value’)).toEqual(‘here’); }); });});
    143. 143. Fake Serverdescribe("Flashcard model", function () { beforeEach(function () { this.Flashcard = require(models/Flashcard); }); describe(“when fetch”, function() { beforeEach(function() { Fake server stub XHR this.server = sinon.fakeServer.create(); this.flashcard = new this.Flashcard({contentId: 174087}); }); afterEach(function() { this.server.restore(); }); it(“should get data from backend”, function() { this.server.respondWith( "GET", "/community/dailylesson/wordiknowupdate.ashx?contentId=174087", [200, { "Content-Type": "application/json" }, { "Value": "here", "Translation": "这儿", "Audio": "here_en.mp3", "IsWord": true, "ContentId": 174087, "IsWordIKnow": false } ] ); this.flashcard.fetch(); this.server.respond(); expect(this.flashcard.get(‘Value’)).toEqual(‘here’); }); });});
    144. 144. Fake Serverdescribe("Flashcard model", function () { beforeEach(function () { this.Flashcard = require(models/Flashcard); }); describe(“when fetch”, function() { beforeEach(function() { this.server = sinon.fakeServer.create(); this.flashcard = new this.Flashcard({contentId: 174087}); }); afterEach(function() { this.server.restore(); }); responds to given URL and HTTP method it(“should get data from backend”, function() { this.server.respondWith( "GET", "/community/dailylesson/wordiknowupdate.ashx?contentId=174087", [200, { "Content-Type": "application/json" }, { "Value": "here", "Translation": "这儿", "Audio": "here_en.mp3", "IsWord": true, "ContentId": 174087, "IsWordIKnow": false } ] ); this.flashcard.fetch(); this.server.respond(); expect(this.flashcard.get(‘Value’)).toEqual(‘here’); }); });});
    145. 145. Fake Serverdescribe("Flashcard model", function () { beforeEach(function () { this.Flashcard = require(models/Flashcard); }); describe(“when fetch”, function() { beforeEach(function() { this.server = sinon.fakeServer.create(); this.flashcard = new this.Flashcard({contentId: 174087}); }); afterEach(function() { this.server.restore(); }); it(“should get data from backend”, function() { this.server.respondWith( given HTTP method "GET", "/community/dailylesson/wordiknowupdate.ashx?contentId=174087", [200, { "Content-Type": "application/json" }, { "Value": "here", "Translation": "这儿", "Audio": "here_en.mp3", "IsWord": true, "ContentId": 174087, "IsWordIKnow": false } ] ); this.flashcard.fetch(); this.server.respond(); expect(this.flashcard.get(‘Value’)).toEqual(‘here’); }); });});
    146. 146. Fake Serverdescribe("Flashcard model", function () { beforeEach(function () { this.Flashcard = require(models/Flashcard); }); describe(“when fetch”, function() { beforeEach(function() { this.server = sinon.fakeServer.create(); this.flashcard = new this.Flashcard({contentId: 174087}); }); afterEach(function() { this.server.restore(); }); it(“should get data from backend”, function() { this.server.respondWith( given URL "GET", "/community/dailylesson/wordiknowupdate.ashx?contentId=174087", [200, { "Content-Type": "application/json" }, { "Value": "here", "Translation": "这儿", "Audio": "here_en.mp3", "IsWord": true, "ContentId": 174087, "IsWordIKnow": false } ] ); this.flashcard.fetch(); this.server.respond(); expect(this.flashcard.get(‘Value’)).toEqual(‘here’); }); });});
    147. 147. Fake Serverdescribe("Flashcard model", function () { beforeEach(function () { this.Flashcard = require(models/Flashcard); }); describe(“when fetch”, function() { beforeEach(function() { this.server = sinon.fakeServer.create(); this.flashcard = new this.Flashcard({contentId: 174087}); }); afterEach(function() { this.server.restore(); }); it(“should get data from backend”, function() { this.server.respondWith( "GET", "/community/dailylesson/wordiknowupdate.ashx?contentId=174087", [200, { "Content-Type": "application/json" }, fake response header { "Value": "here", "Translation": "这儿", "Audio": "here_en.mp3", "IsWord": true, "ContentId": 174087, "IsWordIKnow": false } ] ); this.flashcard.fetch(); this.server.respond(); expect(this.flashcard.get(‘Value’)).toEqual(‘here’); }); });});
    148. 148. Fake Serverdescribe("Flashcard model", function () { beforeEach(function () { this.Flashcard = require(models/Flashcard); }); describe(“when fetch”, function() { beforeEach(function() { this.server = sinon.fakeServer.create(); this.flashcard = new this.Flashcard({contentId: 174087}); }); afterEach(function() { this.server.restore(); }); it(“should get data from backend”, function() { this.server.respondWith( "GET", "/community/dailylesson/wordiknowupdate.ashx?contentId=174087", [200, { "Content-Type": "application/json" }, { "Value": "here", "Translation": "这儿", "Audio": "here_en.mp3", "IsWord": true, "ContentId": 174087, "IsWordIKnow": false } ] ); fake response body this.flashcard.fetch(); this.server.respond(); expect(this.flashcard.get(‘Value’)).toEqual(‘here’); }); });});
    149. 149. Fake Serverdescribe("Flashcard model", function () { beforeEach(function () { this.Flashcard = require(models/Flashcard); }); describe(“when fetch”, function() { beforeEach(function() { this.server = sinon.fakeServer.create(); this.flashcard = new this.Flashcard({contentId: 174087}); }); afterEach(function() { this.server.restore(); }); it(“should get data from backend”, function() { this.server.respondWith( "GET", "/community/dailylesson/wordiknowupdate.ashx?contentId=174087", [200, { "Content-Type": "application/json" }, { "Value": "here", "Translation": "这儿", "Audio": "here_en.mp3", "IsWord": true, "ContentId": 174087, "IsWordIKnow": false } ] ); this.flashcard.fetch(); this.server.respond(); immediately responds with fake data expect(this.flashcard.get(‘Value’)).toEqual(‘here’); }); });});
    150. 150. Fake Serverdescribe("Flashcard model", function () { beforeEach(function () { this.Flashcard = require(models/Flashcard); }); describe(“when fetch”, function() { beforeEach(function() { this.server = sinon.fakeServer.create(); this.flashcard = new this.Flashcard({contentId: 174087}); }); afterEach(function() { this.server.restore(); }); it(“should get data from backend”, function() { this.server.respondWith( "GET", "/community/dailylesson/wordiknowupdate.ashx?contentId=174087", [200, { "Content-Type": "application/json" }, { "Value": "here", "Translation": "这儿", "Audio": "here_en.mp3", "IsWord": true, "ContentId": 174087, "IsWordIKnow": false } ] ); this.flashcard.fetch(); this.server.respond(); expect(this.flashcard.get(‘Value’)).toEqual(‘here’); verify changes }); });});
    151. 151. Next Step• fixture management • DOM • HTTP Response• Code Coverage for Javascript• Integration with CI• Integration with jsTestDriver
    152. 152. Thank you
    153. 153. Thank youif you like this topic please give me
    154. 154. Thank youif you like this topic please give me
    1. A particular slide catching your eye?

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

    ×