Advanced Jasmine - Front-End JavaScript Unit Testing
Upcoming SlideShare
Loading in...5
×
 

Advanced Jasmine - Front-End JavaScript Unit Testing

on

  • 5,611 views

Code: https://github.com/larsthorup/jasmine-demo-advanced ...

Code: https://github.com/larsthorup/jasmine-demo-advanced
Video: https://www.youtube.com/watch?v=g4eQplHxU18
Audio: https://www.youtube.com/watch?v=8FUwc3gZDMw

Unit testing front-end JavaScript presents its own unique set of challenges. In this session we will look at number of different techniques to tackle these challenges and make our JavaScript unit tests fast and robust. We plan to cover the following subjects:

* Mocking and spy techniques to avoid dependencies on
- Functions, methods and constructor functions
- Time (new Date())
- Timers (setTimeout, setInterval)
- Ajax requests
- The DOM
- Events
* Expressive matchers
- Jasmine-jQuery
* Structuring tests for reuse and readability
* Testing browser-specific behaviour

Statistics

Views

Total Views
5,611
Views on SlideShare
5,228
Embed Views
383

Actions

Likes
8
Downloads
37
Comments
0

4 Embeds 383

http://www.zealake.com 376
https://twitter.com 5
https://www.google.it 1
http://www.btbw.pl 1

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Advanced Jasmine - Front-End JavaScript Unit Testing Advanced Jasmine - Front-End JavaScript Unit Testing Presentation Transcript

  • ADVANCED JASMINE FRONT-END JAVASCRIPT UNIT TESTING /Lars Thorup, ZeaLake @larsthorup
  • WHO IS LARS THORUP Software developer/architect C++, C# and JavaScript Test Driven Development Coach: Teaching agile and automated testing Advisor: Assesses software projects and companies Founder and CEO of ZeaLake
  • AGENDA Unit tests gives quality feedback Make them fast Make them precise Run thousands of unit tests in seconds We will look at Mocking techniques Front-end specific testing patterns Assuming knowledge about JavaScript and unit testing
  • JASMINE BASICS describe('Calculator', function () { var calc; beforeEach(function () { calc = new Calculator(); }); it('should multiply', function () { expect(calc.multiply(6, 7)).toBe(42); }); });
  • MOCKING, SPYING AND STUBBING
  • HOW TO TEST IN ISOLATION? We want to test code in isolation here the code is the 'keypress' event handler and isolation means not invoking the getMatch() method 'keypress': function (element, event) { var pattern = this.element.val(); pattern += String.fromCharCode(event.charCode); var match = this.getMatch(pattern); if (match) { event.preventDefault(); this.element.val(match); } }
  • MOCKING METHODS We can mock the getMatch() method decide how the mock should behave verify that the mocked method was called correctly spyOn(autoComplete, 'getMatch').andReturn('monique'); $('#name').trigger($.Event('keypress', {charCode: 109})); expect(autoComplete.getMatch).toHaveBeenCalledWith('m'); expect($('#name')).toHaveValue('monique');
  • MOCKING GLOBAL FUNCTIONS Global functions are properties of the window object openPopup: function (url) { var popup = window.open(url, '_blank', 'resizable'); popup.focus(); } var popup; spyOn(window, 'open').andCallFake(function () { popup = { focus: jasmine.createSpy() }; return popup; }); autoComplete.openPopup('zealake.com'); expect(window.open).toHaveBeenCalledWith('zealake.com', '_blank', 'resizable'); expect(popup.focus).toHaveBeenCalledWith();
  • MOCKING CONSTRUCTORS Constructors are functions with thisbeing the object to construct this.input = new window.AutoComplete(inputElement, { listUrl: this.options.listUrl }); this.input.focus(); spyOn(window, 'AutoComplete').andCallFake(function () { this.focus = jasmine.createSpy(); }); expect(window.AutoComplete.callCount).toBe(1); var args = window.AutoComplete.mostRecentCall.args; expect(args[0]).toBe('#name'); expect(args[1]).toEqual({listUrl: '/someUrl'}); var object = window.AutoComplete.mostRecentCall.object; expect(object.focus).toHaveBeenCalledWith();
  • HOW TO AVOID WAITING? We want the tests to be fast So don't use Jasmine waitsFor() But we often need to wait For animations to complete For AJAX responses to return delayHide: function () { var self = this; setTimeout(function () { self.element.hide(); }, this.options.hideDelay); }
  • MOCKING TIMERS Use Jasmine's mock clock Control the clock explicitly Now the test completes in milliseconds without waiting jasmine.Clock.useMock(); autoComplete.delayHide(); expect($('#name')).toBeVisible(); jasmine.Clock.tick(500); expect($('#name')).not.toBeVisible();
  • MOCKING TIME new Date()tends to return different values over time Actually, that's the whole point :) But how do we test code that does that? We cannot expecton a value that changes on every run We can mock the Date()constructor! var then = new Date(); jasmine.Clock.tick(42000); var now = new Date(); expect(now.getTime() - then.getTime()).toBe(42000);
  • MOCKING DATE() WITH JASMINE Keep Date() and setTimeout() in sync jasmine.GlobalDate = window.Date; var MockDate = function () { var now = jasmine.Clock.defaultFakeTimer.nowMillis; return new jasmine.GlobalDate(now); }; MockDate.prototype = jasmine.GlobalDate.prototype; window.Date = MockDate; jasmine.getEnv().currentSpec.after(function () { window.Date = jasmine.GlobalDate; });
  • MOCKING AJAX REQUESTS To test in isolation To vastly speed up the tests Many options can.fixture Mockjax Sinon can.fixture('/getNames', function (original, respondWith) { respondWith({list: ['rachel', 'lakshmi']}); }); autoComplete = new AutoComplete('#name', { listUrl: '/getNames' }); jasmine.Clock.tick(can.fixture.delay); respondWith(500); // Internal server error
  • DOM FIXTURES Supply the markup required by the code Automatically cleanup markup after every test Various solutions Built into QUnit as #qunit-fixture Use jasmine-jquery var fixtures = jasmine.getFixtures(); fixtures.set(fixtures.sandbox()); $('<input id="name">').appendTo('#sandbox'); autoComplete = new AutoComplete('#name');
  • SPYING ON EVENTS How do we test that an event was triggered? Or prevented from bubbling? Use jasmine-jquery! 'keypress': function (element, event) { var pattern = this.element.val() + String.fromCharCode(event.charCode); var match = this.getMatch(pattern); if(match) { event.preventDefault(); this.element.val(match); } } keypressEvent = spyOnEvent('#name', 'keypress'); $('#name').trigger($.Event('keypress', {charCode: 109})); expect(keypressEvent).toHaveBeenPrevented();
  • SIMULATING CSS TRANSITIONS
  • JASMINE MATCHERS
  • EXPRESSIVE MATCHERS Make your tests more readable Use jasmine-jquery for jQuery-specific matchers Instead of: Prefer: expect($('#name').is(':visible')).toBeFalsy(); expect($('#name')).not.toBeVisible();
  • ROLL YOUR OWN MATCHERS Make your tests even more readable Like this can.js specific matcher: Defined like this: github.com/pivotal/jasmine/wiki/Matchers expect($('#name')).toHaveControlOfType(AutoComplete); jasmine.getEnv().currentSpec.addMatchers({ toHaveControlOfType: function (expected) { var actual = this.actual.controls(expected); return actual.length > 0; } });
  • STRUCTURE OF TEST CODE Reuse common setup code By nesting Jasmine's describe()functions describe('delayHide', function () { beforeEach(function () { autoComplete.delayHide(); }); it('should initially stay visible', function () { expect($('#name')).toBeVisible(); }); describe('after a delay', function () { beforeEach(function () { jasmine.Clock.tick(500); }); it('should be invisible', function () { expect($('#name')).not.toBeVisible(); }); }); });
  • BROWSER-SPECIFIC TESTS Some code is browser specific maybe using a browser specific API and might only be testable in that browser Tests can be conditioned Or iterated... can.each([ { response: {list: ['rachel', 'lakshmi']}, expected: ['rachel', 'lakshmi'] }, { response: 500, expected: [] } ], function (scenario) { describe('when ' + JSON.stringify(scenario.response), function () { it('should ' + JSON.stringify(scenario.expected), function () { }); }); });
  • RESOURCES github.com/larsthorup/jasmine-demo-advanced @larsthorup pivotal.github.io/jasmine github.com/velesin/jasmine-jquery canjs.com github.com/hakimel/reveal.js