0
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 a...
AGENDA
Unit tests gives quality feedback
Make them fast
Make them precise
Run thousands of unit tests in seconds
We will l...
JASMINE BASICS
describe('Calculator', function () {
var calc;
beforeEach(function () {
calc = new Calculator();
});
it('sh...
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 me...
MOCKING METHODS
We can mock the getMatch() method
decide how the mock should behave
verify that the mocked method was call...
MOCKING GLOBAL FUNCTIONS
Global functions are properties of the window object
openPopup: function (url) {
var popup = wind...
MOCKING CONSTRUCTORS
Constructors are functions
with thisbeing the object to construct
this.input = new window.AutoComplet...
HOW TO AVOID WAITING?
We want the tests to be fast
So don't use Jasmine waitsFor()
But we often need to wait
For animation...
MOCKING TIMERS
Use Jasmine's mock clock
Control the clock explicitly
Now the test completes in milliseconds
without waitin...
MOCKING TIME
new Date()tends to return different values over time
Actually, that's the whole point :)
But how do we test c...
MOCKING DATE() WITH JASMINE
Keep Date() and setTimeout() in sync
jasmine.GlobalDate = window.Date;
var MockDate = function...
MOCKING AJAX REQUESTS
To test in isolation
To vastly speed up the tests
Many options
can.fixture
Mockjax
Sinon
can.fixture...
DOM FIXTURES
Supply the markup required by the code
Automatically cleanup markup after every test
Various solutions
Built ...
SPYING ON EVENTS
How do we test that an event was triggered?
Or prevented from bubbling?
Use jasmine-jquery!
'keypress': f...
SIMULATING CSS TRANSITIONS
JASMINE MATCHERS
EXPRESSIVE MATCHERS
Make your tests more readable
Use jasmine-jquery for jQuery-specific matchers
Instead of:
Prefer:
expe...
ROLL YOUR OWN MATCHERS
Make your tests even more readable
Like this can.js specific matcher:
Defined like this:
github.com...
STRUCTURE OF TEST CODE
Reuse common setup code
By nesting Jasmine's describe()functions
describe('delayHide', function () ...
BROWSER-SPECIFIC TESTS
Some code is browser specific
maybe using a browser specific API
and might only be testable in that...
RESOURCES
github.com/larsthorup/jasmine-demo-advanced
@larsthorup
pivotal.github.io/jasmine
github.com/velesin/jasmine-jqu...
Upcoming SlideShare
Loading in...5
×

Advanced Jasmine - Front-End JavaScript Unit Testing

11,478

Published on

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

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

No Downloads
Views
Total Views
11,478
On Slideshare
0
From Embeds
0
Number of Embeds
4
Actions
Shares
0
Downloads
78
Comments
0
Likes
15
Embeds 0
No embeds

No notes for slide

Transcript of "Advanced Jasmine - Front-End JavaScript Unit Testing"

  1. 1. ADVANCED JASMINE FRONT-END JAVASCRIPT UNIT TESTING /Lars Thorup, ZeaLake @larsthorup
  2. 2. 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
  3. 3. 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
  4. 4. JASMINE BASICS describe('Calculator', function () { var calc; beforeEach(function () { calc = new Calculator(); }); it('should multiply', function () { expect(calc.multiply(6, 7)).toBe(42); }); });
  5. 5. MOCKING, SPYING AND STUBBING
  6. 6. 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); } }
  7. 7. 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');
  8. 8. 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();
  9. 9. 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();
  10. 10. 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); }
  11. 11. 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();
  12. 12. 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);
  13. 13. 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; });
  14. 14. 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
  15. 15. 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');
  16. 16. 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();
  17. 17. SIMULATING CSS TRANSITIONS
  18. 18. JASMINE MATCHERS
  19. 19. 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();
  20. 20. 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; } });
  21. 21. 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(); }); }); });
  22. 22. 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 () { }); }); });
  23. 23. RESOURCES github.com/larsthorup/jasmine-demo-advanced @larsthorup pivotal.github.io/jasmine github.com/velesin/jasmine-jquery canjs.com github.com/hakimel/reveal.js
  1. A particular slide catching your eye?

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

×