Advanced Javascript Unit Testing

13
Advanced JavaScript Unit Testing Lars Thorup ZeaLake Software Consulting April, 2015

Transcript of Advanced Javascript Unit Testing

Page 1: Advanced Javascript Unit Testing

Advanced JavaScriptUnit TestingLars ThorupZeaLake Software Consulting

April, 2015

Page 2: Advanced Javascript Unit Testing

Who is Lars Thorup?

● Software developer/architect● JavaScript, C#● Test Driven Development● Continuous Integration

● Coach: Teaching TDD and continuous integration

● Founder of ZeaLake

● @larsthorup

Page 3: Advanced Javascript Unit Testing

Best practices for JavaScript unit testing● Asynchronous code: callbacks and promises

● Time and timers

● The DOM

● Ajax

● Cross-browser testing

● Leak detection

● Continuous testing

Page 4: Advanced Javascript Unit Testing

Asynchronous code: callbacks● How to wait for the callback?

● Call done() when done! [Mocha, Jasmine]

https://github.com/larsthorup/mha2015-demo

it('should eventually return a+b', function () { code.addSlow(4, 5, function (result) { result.should.equal(9); });});

it('should eventually return a+b', function (done) { code.addSlow(4, 5, function (result) { result.should.equal(9); done(); });});

When this is called the test has

already ended

Page 5: Advanced Javascript Unit Testing

Asynchronous code: promises● Return a promise from the test [Mocha]

● Use promise matchers [chai-as-promised]

it('should eventually return a+b', function () { return code.adding(4, 5).should.become(9);});

https://github.com/larsthorup/mha2015-demo

Page 6: Advanced Javascript Unit Testing

Time and timers● How to test delayed behavior fast?

● Fake the built-in timer! [Sinon, Jasmine]

beforeEach(function () { this.sinon = sinon.sandbox.create(); this.sinon.useFakeTimers();});

afterEach(function () { this.sinon.restore();});

it('should eventually return a+b', function () { var adding = code.adding(4, 5); this.sinon.clock.tick(500); return adding.should.become(9);});

https://github.com/larsthorup/mha2015-demo

Page 7: Advanced Javascript Unit Testing

The DOM● Don't copy real markup

● Use the real thing if fast+easy (client side templating, RequireJS)● Else inject minimal fake markup

● Use a fragment, or insert a fixture, don't use document

https://github.com/larsthorup/jsdevenv-mocha-require/blob/master/src/test/js/page/weather.test.js

it('listens', function () { var context = $('<div><input type="text" id="city" /></div>'); weather.listen(context); var city = context.find('#city'); city.val('San Francisco'); city.trigger('change'); expect(weather.fetch.calledWith('San Francisco')).to.equal(true);});

Page 8: Advanced Javascript Unit Testing

CSS / responsive design● Render into an iframe, then adjust size [quixote]

beforeEach(function () { iframe = $('<iframe></iframe>').appendTo(fixture); context = $(iframe.get(0).contentDocument); $('<style></style>').text(menuCss).appendTo(context.find('head')); var menu = $(multiline(function () {/* <ul class="menu"> <li>Item 1</li> <li>Item 2</li> </ul> */})).appendTo(context.find('body')); items = menu.find('li');});

it('should turn horizontal when wide', function () { iframe.attr('width', '401px'); expect(items.eq(0).offset().left).to.be.below(items.eq(1).offset().left); expect(items.eq(0).offset().top).to.equal(items.eq(1).offset().top);});

https://github.com/larsthorup/jsdevenv-mocha-require/blob/master/src/test/js/style/menu-responsive.test.js

Page 9: Advanced Javascript Unit Testing

Ajax● Don't call any services

● Slow, fragile, setup heavy

● Mock or fake server responses [Sinon, mockjax]

https://github.com/larsthorup/jsdevenv-mocha-require/blob/master/src/test/js/page/weather.test.js

it('fetches', function (done) { $.mockjax({ url: 'http://api.openweathermap.org/data/2.5/weather', data: {q: 'Denver'}, responseText: { weather: [{ description: 'sun' }] }, responseTime: 1 }); var fetching = weather.fetch('Denver'); fetching.then(function (data) { expect(data.text).to.equal('sun'); done(); });});

Page 10: Advanced Javascript Unit Testing

Ajax - generate mocks from real responses

● zealake.com/2015/01/05/unit-test-your-service-integration-layer/

https://github.com/larsthorup/mars/blob/master/test/util/api.proxy.js

https://github.com/larsthorup/mars/tree/master/demo/test/api-faker.js

Page 11: Advanced Javascript Unit Testing

Cross-browser testing● Use Karma to run your tests in real browsers

● Also valuable for debugging

● Use browser services for continuous integration● BrowserStack● SauceLabs

https://github.com/larsthorup/jsdevenv-mocha-require/blob/master/Gruntfile.js

Page 12: Advanced Javascript Unit Testing

Leak detection● To ensure that tests don't influence each other

● To make our code more reusable

● Use global beforeEach() and afterEach() [Mocha, Jasmine]

● Sample before, verify after● window / global properties● DOM elements● localstorage keys

Page 13: Advanced Javascript Unit Testing

Wallaby.js● Continuous Testing

● For JetBrains IDE's● Visual Studio is in progress

● wallabyjs.com