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

Automated Testing

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); });