Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Writing testable JS
by Ted Piotrowski
Javascript Ho Chi Minh City
Excuses for not testing
● I know the code
● Test suite is hard to configure and run
● You can’t test UI
● You can’t unit t...
$(document).ready(function() {
$('#new-status form').submit(function(e) {
e.preventDefault();
$.ajax({
url: '/status',
typ...
Documentation
● Impossible to write a good test without a
good specification
● If you don’t have time to write a test, at ...
/**
* adds two numbers together
*/
function sum(a, b)
assert(sum(1, 2), 3);
/**
* adds two numbers together
*/
function sum(a, b)
assert(sum(1, ‘a’), ?);
/**
* adds two numbers together,
* otherwise returns null
*/
function sum(a, b)
assert(sum(1, ‘a’), null);
Dependencies
● Can’t write good tests unless you
understand what external objects the code
depends on
● loose coupling
● u...
Scope
● An assertion should only rely on the method
being tested
● What is a “unit”?
● is $(function() { }) a unit?
// Should we stub addTwo, addOne?
// When we limit scope, we forfeit integration
function addThree(a) {
var x = addTwo(a);...
Testing with the DOM
● Use a DOM fragment / jQuery fragment
● Inject it into the module constructor
function Word() {
this.el = $('.word'); // external dependency
}
Word.prototype.setText = function(text) {
this.el.text(te...
function Word(el) {
this.el = el; // dependency injection
}
Word.prototype.setText = function(text) {
this.el.text(text);
...
function Word(el) {
this.el = el || $('.word'); // optional injection
}
Word.prototype.setText = function(text) {
this.el....
Dealing with window properties
● You code will likely touch Web API
○ document, Math,
● Can use mocks and stubs for method...
// If your production code calls alert(), you can mock it
var spy = sinon.spy(window, "alert");
assert(spy.calledWith("My ...
function Win(windowObj) {
this.window = windowObj || window;
}
Win.prototype.scrollX = function() { return this.window.scr...
Dealing with network calls
● Use sinon fake server
{
"test should fetch comments from server" : function () {
this.server.respondWith("GET", "/some/article/comments.json",
[...
Code coverage
● Reports what lines of production JS are
executed during testing
● necessary, but not sufficient
● Istanbul...
JS integration tests
● main.js file initializes your components and
injects DOM dependencies
● Stub your REST calls
● Just...
// Bootstrap your application here
// Inject your DOM dependencies at top of call stack
// Allows you to mock/stub the DOM...
Demo time
More information
● Testable Javascript
● Karma test runner
● Sinon.js
● Istanbul
About us
Author: Ted Piotrowski
Find me at: tpiotrowski@atlassian.com
Sample code: https://bitbucket.org/tpiotrowski/js-hc...
Lean Coffee
Discussion topics
Upcoming SlideShare
Loading in …5
×

Writing testable js [by Ted Piotrowski]

855 views

Published on

About us
Author: Ted Piotrowski
Find me at: tpiotrowski@atlassian.com
Sample code: https://bitbucket.org/tpiotrowski/js-hcm

Presentation made for Javascript Ho Chi Minh City Meetup Group

You can find us at:
http://www.meetup.com/JavaScript-Ho-Chi-Minh-City/
https://www.facebook.com/JavaScriptHCMC
https://plus.google.com/u/0/communities/116105314977285194967

Published in: Technology
  • Be the first to comment

  • Be the first to like this

Writing testable js [by Ted Piotrowski]

  1. 1. Writing testable JS by Ted Piotrowski Javascript Ho Chi Minh City
  2. 2. Excuses for not testing ● I know the code ● Test suite is hard to configure and run ● You can’t test UI ● You can’t unit test JS code
  3. 3. $(document).ready(function() { $('#new-status form').submit(function(e) { e.preventDefault(); $.ajax({ url: '/status', type: 'POST', dataType: 'json', data: { text: $('#new-status').find('textarea').val() }, success: function(data) { $('#statuses').append('<li>' + data.text + '</li>'); $('#new-status').find('textarea').val(''); } }); }); }); source: https://github.com/kjbekkelund/writings/blob/master/published/understanding-backbone.md/
  4. 4. Documentation ● Impossible to write a good test without a good specification ● If you don’t have time to write a test, at least write documentation ○ it will allow others to write tests later
  5. 5. /** * adds two numbers together */ function sum(a, b) assert(sum(1, 2), 3);
  6. 6. /** * adds two numbers together */ function sum(a, b) assert(sum(1, ‘a’), ?);
  7. 7. /** * adds two numbers together, * otherwise returns null */ function sum(a, b) assert(sum(1, ‘a’), null);
  8. 8. Dependencies ● Can’t write good tests unless you understand what external objects the code depends on ● loose coupling ● use requirejs, almond.js, squire.js ● move dependencies up the call stack and inject
  9. 9. Scope ● An assertion should only rely on the method being tested ● What is a “unit”? ● is $(function() { }) a unit?
  10. 10. // Should we stub addTwo, addOne? // When we limit scope, we forfeit integration function addThree(a) { var x = addTwo(a); var y = addOne(x); return y; }
  11. 11. Testing with the DOM ● Use a DOM fragment / jQuery fragment ● Inject it into the module constructor
  12. 12. function Word() { this.el = $('.word'); // external dependency } Word.prototype.setText = function(text) { this.el.text(text); }; var word = new Word(); word.setText('Hello World'); assert(???, 'Hello World');
  13. 13. function Word(el) { this.el = el; // dependency injection } Word.prototype.setText = function(text) { this.el.text(text); }; var mockEl = $('<div></div>'); // use a test double var word = new Word(mockEl); // inject the dependency word.setText('Hello World'); assert(mockEl.text(), 'Hello World');
  14. 14. function Word(el) { this.el = el || $('.word'); // optional injection } Word.prototype.setText = function(text) { this.el.text(text); }; var mockEl = $('<div></div>'); var word = new Word(mockEl); word.setText('Hello World'); assert(mockEl.text(), 'Hello World');
  15. 15. Dealing with window properties ● You code will likely touch Web API ○ document, Math, ● Can use mocks and stubs for methods ● Many Web API properties are read-only
  16. 16. // If your production code calls alert(), you can mock it var spy = sinon.spy(window, "alert"); assert(spy.calledWith("My alert message")); // However, you can't modify read only properties Math.PI = 3.15; // won’t work window.History.length = 12;
  17. 17. function Win(windowObj) { this.window = windowObj || window; } Win.prototype.scrollX = function() { return this.window.scrollX }; Win.prototype.scrollY = function() { return this.window.scrollY }; var win = new Win(); // in production // win.scrollX() => actual value var win = new Win({ // in testing scrollX: 50, scrollY: 40, History: { … } }); // win.scrollX() => 50
  18. 18. Dealing with network calls ● Use sinon fake server
  19. 19. { "test should fetch comments from server" : function () { this.server.respondWith("GET", "/some/article/comments.json", [200, { "Content-Type": "application/json" }, '[{ "id": 12, "comment": "Hey there" }]']); var callback = sinon.spy(); myLib.getCommentsFor("/some/article", callback); this.server.respond(); sinon.assert.calledWith(callback, [{ id: 12, comment: "Hey there" }])); } }
  20. 20. Code coverage ● Reports what lines of production JS are executed during testing ● necessary, but not sufficient ● Istanbul is a good tool ○ integrates with Karma
  21. 21. JS integration tests ● main.js file initializes your components and injects DOM dependencies ● Stub your REST calls ● Just like unit testing, user events are your input, DOM changes are your output
  22. 22. // Bootstrap your application here // Inject your DOM dependencies at top of call stack // Allows you to mock/stub the DOM for each component $(function() { var $el = $('.some-element'); var component = new Component($el); var $el2 = $('.some-element2'); var component2 = new Component($el2); // initialize more components, global state here });
  23. 23. Demo time
  24. 24. More information ● Testable Javascript ● Karma test runner ● Sinon.js ● Istanbul
  25. 25. About us Author: Ted Piotrowski Find me at: tpiotrowski@atlassian.com Sample code: https://bitbucket.org/tpiotrowski/js-hcm Presentation made for Javascript Ho Chi Minh City Meetup Group You can find us at: ● http://www.meetup.com/JavaScript-Ho-Chi-Minh-City/ ● https://www.facebook.com/JavaScriptHCMC ● https://plus.google.com/u/0/communities/116105314977285194967 ● http://www.slideshare.net/JavascriptMeetup
  26. 26. Lean Coffee Discussion topics

×