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

Automated Testing

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.

Automated Testing

An introduction to testing JavaScript

Avatar for Blake Haswell

Blake Haswell

July 22, 2014
Tweet

Other Decks in Programming

Transcript

  1. Why? • Increased developer confidence. • Automated tests are more

    reliable than manual tests. • Automated tests can test what manual tests cannot. • Tests encourage incremental development. • Free QA experts to do more interesting and valuable work. • You’re already doing it…
  2. “Whenever you are tempted to type something into a print

    statement or a debugger expression, write it as a test instead.” —Martin Fowler
  3. describe('fscsw.common.getAge', function () { ! it( 'calculates someone’s age, given

    a birth date', function () { expect(fscsw.common.getAge(1988, 5, 5)) .to.equal(26); } ); ! });
  4. describe('fscsw.common.getAge', function () { ! it( 'calculates someone’s age, given

    a birth date', function () { expect(fscsw.common.getAge(1988, 5, 5)) .to.equal(26); } ); ! });
  5. describe('fscsw.common.getAge', function () { ! it( 'calculates someone’s age, given

    a birth date', function () { expect(fscsw.common.getAge(1988, 5, 5)) .to.equal(26); } ); ! });
  6. describe('fscsw.common.getAge', function () { ! it( 'calculates someone’s age, given

    a birth date', function () { expect(fscsw.common.getAge(1988, 5, 5)) .to.equal(26); } ); ! });
  7. describe('fscsw.common.getAge', function () { ! it( 'calculates someone’s age, given

    a birth date', function () { expect(fscsw.common.getAge(1988, 5, 5)) .to.equal(26); } ); ! });
  8. Stubs Stubs are functions with pre- programmed behaviour. They are

    used to simulate the behaviour of a module’s dependencies while it is under test.
  9. describe('fscsw.common.getAge', function () { ! beforeEach(function () { // 2014-07-18

    this.clock = sinon.useFakeTimers(1405656532419); }); ! afterEach(function () { this.clock.restore(); }); it( 'calculates someone’s age, given a birth date', function () { expect(fscsw.common.getAge(1988, 5, 5)) .to.equal(26); } ); ! });
  10. describe('fscsw.common.getAge', function () { ! beforeEach(function () { // 2014-07-18

    this.clock = sinon.useFakeTimers(1405656532419); }); ! afterEach(function () { this.clock.restore(); }); it( 'calculates someone’s age, given a birth date', function () { expect(fscsw.common.getAge(1988, 5, 5)) .to.equal(26); } ); ! });
  11. Integration Tests Higher level tests which focus on testing that

    larger parts of the system work together to produce the expected behaviour.
  12. describe('match-predictor-share', function () { ! beforeEach(function (done) { // Inject

    element on the page. this.element = $('<fscsw-matchpredictorshare sport="football" code="WC" matchid="WC20140101BRACRO" />'); this.element.appendTo('body'); fscsw.init(this.element); ! // Wait for widget to initialise. waitFor(function () { return this.element.children().length; }.bind(this), done); }); ! afterEach(function () { this.element.trigger('delete'); this.element.remove(); }); ! it('displays the names of the teams playing the match', function () { var titles = this.element.find('.fscsw-team-name'); expect(titles).to.have.length(2); expect(titles[0].innerHTML).to.equal('Brazil'); expect(titles[1].innerHTML).to.equal('Croatia'); }); ! });
  13. describe('match-predictor-share', function () { ! beforeEach(function (done) { // Inject

    element on the page. this.element = $('<fscsw-matchpredictorshare sport="football" code="WC" matchid="WC20140101BRACRO" />'); this.element.appendTo('body'); fscsw.init(this.element); ! // Wait for widget to initialise. waitFor(function () { return this.element.children().length; }.bind(this), done); }); ! afterEach(function () { this.element.trigger('delete'); this.element.remove(); }); ! it('displays the names of the teams playing the match', function () { var titles = this.element.find('.fscsw-team-name'); expect(titles).to.have.length(2); expect(titles[0].innerHTML).to.equal('Brazil'); expect(titles[1].innerHTML).to.equal('Croatia'); }); ! });
  14. describe('match-predictor-share', function () { ! beforeEach(function (done) { // Inject

    element on the page. this.element = $('<fscsw-matchpredictorshare sport="football" code="WC" matchid="WC20140101BRACRO" />'); this.element.appendTo('body'); fscsw.init(this.element); ! // Wait for widget to initialise. waitFor(function () { return this.element.children().length; }.bind(this), done); }); ! afterEach(function () { this.element.trigger('delete'); this.element.remove(); }); ! it('displays the names of the teams playing the match', function () { var titles = this.element.find('.fscsw-team-name'); expect(titles).to.have.length(2); expect(titles[0].innerHTML).to.equal('Brazil'); expect(titles[1].innerHTML).to.equal('Croatia'); }); ! });
  15. describe('match-predictor-share', function () { ! beforeEach(function (done) { // Mock

    request. var origStart = fscsw.request._start; sinon.stub(fscsw.request, '_start', function () { var args = Array.prototype.slice.call(arguments); if (args[1] === 'http://api.stats.foxsports.com.au/3.0/api/sports/' + 'football/matches/WC20140101BRACRO/scoreboard.json') { args[4](require('../api-responses/scoreboard-football-success.json')); } else { origStart.apply(fscsw.request, args); } }); ! // Inject element on the page. this.element = $('<fscsw-matchpredictorshare sport="football" code="WC" matchid="WC20140101BRACRO" />'); this.element.appendTo('body'); fscsw.init(this.element); ! // Wait for widget to initialise. waitFor(function () { return this.element.children().length; }.bind(this), done); }); ! afterEach(function () { this.element.trigger('delete'); this.element.remove(); fscsw.request._start.restore(); }); ! it('displays the names of the teams playing the match', function () { var titles = this.element.find('.fscsw-team-name'); expect(titles).to.have.length(2); expect(titles[0].innerHTML).to.equal('Brazil'); expect(titles[1].innerHTML).to.equal('Croatia'); }); ! });
  16. describe('match-predictor-share', function () { ! beforeEach(function (done) { // Mock

    request. var origStart = fscsw.request._start; sinon.stub(fscsw.request, '_start', function () { var args = Array.prototype.slice.call(arguments); if (args[1] === 'http://api.stats.foxsports.com.au/3.0/api/sports/' + 'football/matches/WC20140101BRACRO/scoreboard.json') { args[4](require('../api-responses/scoreboard-football-success.json')); } else { origStart.apply(fscsw.request, args); } }); ! // Inject element on the page. this.element = $('<fscsw-matchpredictorshare sport="football" code="WC" matchid="WC20140101BRACRO" />'); this.element.appendTo('body'); fscsw.init(this.element); ! // Wait for widget to initialise. waitFor(function () { return this.element.children().length; }.bind(this), done); }); ! afterEach(function () { this.element.trigger('delete'); this.element.remove(); fscsw.request._start.restore(); }); ! it('displays the names of the teams playing the match', function () { var titles = this.element.find('.fscsw-team-name'); expect(titles).to.have.length(2); expect(titles[0].innerHTML).to.equal('Brazil'); expect(titles[1].innerHTML).to.equal('Croatia'); }); ! });
  17. Why UTs? •Unit tests are FAST. •Unit tests are (mostly)

    isolated – it is easier to test different branches of code. •Unit tests are well suited to code which has a well-defined input and output.
  18. Why Not UTs? •Code with lots of dependencies can be

    hard to unit test. :-( •UI code can be hard to unit test.
  19. Why ITs? •Integration tests make it easy to test UI

    code. •Integration tests increase your confidence that software works end-to-end. •Integration tests are a good way to increase your test coverage.
  20. Why Not ITs? •Integration tests are slower than unit tests.

    •Integration tests may require a lot of setup and teardown code. •Integration tests are poorly suited to testing edge-cases
  21. When to Write Tests •Before fixing a bug •Before adding

    a new feature •Before refactoring existing code
  22. When to Run Tests •After every code change. •Before every

    commit. •Before merging code into a shared branch. •Whenever a branch changes on origin (CI).
  23. Code Coverage • Identify untested code. • Untested code is

    correlated with defect count. • Coverage is not necessarily correlated with quality.
  24. it('calculates someone’s age, given a birth date', function () {

    expect(fscsw.common.getAge(1988, 5, 5)).to.equal(26); expect(fscsw.common.getAge(1988, 7, 18)).to.equal(26); expect(fscsw.common.getAge(1988, 7, 19)).to.equal(25); expect(fscsw.common.getAge(1988, 8, 13)).to.equal(25); });