SlideShare a Scribd company logo
JQUERY & 10,000
GLOBAL FUNCTIONS
Working with Legacy JavaScript
WHO IS THIS
GUY?
@guyroyse
guy@guyroyse.com
guyroyse.com
jQuery & 10,000 Global Functions: Working with Legacy JavaScript
Presentation Business Logic Datastore
Presentation Business Logic Data Access
Our Concern The Mapping Their Concern
Presentation Business Logic Data Access
WHAT DOES THIS HAVE TO
DO WITH JAVASCRIPT?
STRATEGY VS DRIFT
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0;">
<title>GuyRoyse.com | Blog</title>
<link rel="icon" type="image/jpg" href="/images/alien-sunrise-icon.png">
<link rel="stylesheet" href=“/site.css">
<script>
💩</script>
</head>
<body>
…
</body>
</html>
SOLUTION #1: WE NEED A FRAMEWORK!
SOLUTION #2: WE NEED A REWRITE!
A new tiger team is selected. Everyone wants to be on this team because it’s a
green-field project. They get to start over and create something truly beautiful.
But only the best and brightest are chosen for the tiger team. Everyone else must
continue to maintain the current system.
Now the two teams are in a race. The tiger team must build a new system that
does everything that the old system does. Not only that, they have to keep up
with the changes that are continuously being made to the old system.
Management will not replace the old system until the new system can do
everything that the old system does.
This race can go on for a very long time. I’ve seen it take 10 years. And by the
time it’s done, the original members of the tiger team are long gone, and the
current members are demanding that the new system be redesigned because it’s
such a mess.
Martin, Robert C. (2008-08-01). Clean Code: A Handbook of Agile Software Craftsmanship (p. 5).
Pearson Education (USA). Kindle Edition.
THE REAL SOLUTION
Untestable
Code
Testable
Code
Tested
Code
TDD
Very Careful
Refactor
Characterization
Tests
Failing Test
WHAT’S A 27-LETTER WORD FOR “CORN”?
THE SIZZLE
I have code in the HTML or in an associated
JavaScript file that does some sort of fancy UI thing
in a dated way.
Problem
Solution Refactor to use modern CSS and test manually.
There is very little point in putting automated tests
around visual effects.
index.html
<script>
function addDropShadow(tag) {
tag.style.backgroundColor = "black";
}
function removeDropShadow(tag) {
tag.style.backgroundColor = "";
}
</script>
<a onmouseenter="addDropShadow(this)" onmouseleave=“removeDropShadow(this)"
href="somepage.jsp">Rollover Test</a>
index.html
<style>
.rollover:hover {
background-color: black;
}
</style>
<a class="rollover">Rollover Test</a>
CODE IN THE HTML
I have code in the HTML. It cannot be loaded into a
test harness to test it.
Problem
Solution Move code a .js file and add a script tag if needed.
Surround code in question with a test if possible or
extract a method and test that. If there is code in
event attributes in the HTML, used jQuery to bind
to those events instead.
index.html
<script>
function doAwesomeThing() {
thing.doAwesomeStuff("Awesome App is Awesome");
}
</script>
<a onclick="doAwesomeThing()">Awesome Thing</a>
index.html
<script src="awesome.js"></script>
<a id="awesomeThing">Awesome Thing</a>
awesome.js
$('#awesomeThing').on('click', onAwesomeThingClicked);
function onAwesomeThingClicked() {
thing.doAwesomeStuff("Awesome App is Awesome");
}
awesome-spec.js
describe("awesome thing", function() {
beforeEach(function() {
spyOn(thing, 'doAwesomeStuff');
});
it("does awesome stuff when clicked", function() {
onAwesomeThingClicked();
expect(thing.doAwesomeStuff).toHaveBeenCalledWith('Awesome App is Awesome');
});
});
DOM INTERACTION
A lot of the code interacts with the DOM and I
don’t have a DOM in my unit test.
Problem
Solution You can have a DOM in your unit tests. Mock out
HTML elements using Jasmine jQuery. Use jQuery
to trigger events and to validate that the DOM is
modified in the expected way.
index.html
<script src="awesome.js"></script>
<a id="awesomeThing">Awesome Thing</a>
<div id="toBeAwesome"/>
awesome.js
$('#awesomeThing').on('click', onAwesomeThingClicked);
function onAwesomeThingClicked() {
$('#toBeAwesome').html("Awesome App is Awesome");
}
awesome-spec.js
describe("awesome thing", function() {
beforeEach(function() {
setFixtures('<div id="toBeAwesome"/>');
});
it("does awesome stuff when clicked", function() {
onAwesomeThingClicked();
expect($('#toBeAwesome')).toHaveHtml('Awesome App is Awesome');
});
});
10,000 GLOBAL FUNCTIONS
I have functions everywhere, man!Problem
Solution Put a test around the global function. Move the
global function to a Module and replace the global
function with a variable that points to the Module.
Later, when no more code is accessing the global,
remove the global variable.
app.js
function transmogrify() {};
function transmute() {};
function transform() {};
app.js
var Mutators = {};
Mutators.transmogrify = function() {};
Mutators.transmute = function() {};
Mutators.transform = function() {};
var transmogrify = Mutators.transmogrify();
var transmute = Mutators.transmute();
var transform = Mutators.transform();
app-spec.js
describe("Mutators.transform", function() {
it("has mapping to legacy function", function() {
expect(transform).toBe(Mutators.transform);
});
it("does stuff", function() {
expect(Mutators.transform()).toBe(true);
});
});
NO UNITS
There are no units to test.Problem
Solution Carefully extract a function and place tests around
that function. Be careful not to create a 10,000
Global Functions problem.
app.js
$(function() {
var id = $('#id').val();
$.get('data/' + id, function(data) {
$('#name').val(data.name);
$('#rank').val(data.rank);
$('#serialNo').val(data.serial);
});
});
app.js
$(function() {
fetchAndUpdateDom();
});
function fetchAndUpdateDom() {
var id = $('#id').val();
$.get('data/' + id, function(data) {
updateDom(data);
});
};
function updateDom(data) {
$('#name').val(data.name);
$('#rank').val(data.rank);
$('#serialNo').val(data.serial);
}
JQUERY EVERYWHERE
I have jQuery calls inline everywhere in my code.Problem
Solution Using the Module pattern, migrate the jQuery that
interacts with the DOM into Passive Views. Migrate
the jQuery that makes AJAX calls into Adapters.
Place tests around these Modules.
app.js
function fetchAndUpdateDom() {
var id = $('#id').val();
$.get('data/' + id, function(data) {
$('#name').val(data.name);
$('#rank').val(data.rank);
$('#serialNo').val(data.serial);
});
};
app.js
var view = {
id : function() { return $('#id').val(); },
name : function(val) { $('#name').val(val); },
rank : function(val) { $('#rank').val(val); },
serialNo : function(val) { $('#serialNo').val(val); }
};
var data = {
fetch : function(id, callback) {
$.get('data/' + id, callback);
}
};
function fetchAndUpdateDom() {
data.fetch(view.id(), function(data) {
view.name(data.name); view.rank(data.rank); view.serialNo(data.serialNo);
});
}
REALLY UGLY FOR LOOPS
These nested for loops are hideous!Problem
Solution Use Underscore, lodash, or the built in array
functions like forEach, find, reduce, and map.
app.js
var values = [1,2,3,4,5], sum = 0, evens = [], doubles = [], i;
for(i = 0; i < values.length; i++) {
sum += values[i]
}
for(i = 0; i < values.length; i++) {
if (values[i] % 2 == 0) evens.push(values[i]);
}
for(i = 0; i < values.length; i++) {
doubles.push(values[i] *2);
}
app.js
var values = [1,2,3,4,5];
var sum = values.reduce(function(prev, value) {
return prev + value;
}, 0);
var evens = values.filter(function(value) {
return value % 2 === 0;
});
var doubles = values.map(function(value) {
return value * 2;
});
REALLY LONG FUNCTIONS
This function is too long!Problem
Solution Place a test around the function and refactor it to a
Revealing Module.
app.js
function doMaths(x, y) {
var add = x + y;
var sub = x - y;
var multi = x * y;
var div = x / y;
return { add: add, sub: sub, multi: multi, div: div };
};
app.js
var mather = (function() {
var _x, _y;
function doMaths(x, y) {
_x = x; _y = y;
return { add: add(), sub: sub(), multi: multi(), div: div() };
}
function add() { return _x + _y; }
function sub() { return _x - _y; }
function multi() { return _x * _y; }
function div() { return _x / _y; }
return { doMaths : doMaths };
})();
mather.doMaths(x, y);
ALMOST DUPLICATION
I have all these functions that do almost the same
thing but I can’t break them down.
Problem
Solution Use a Lambda Factory to create the functions for
you.
app.js
function getFirstNumberTimes(x) { return Number($('#first').val()) * x; }
function getSecondNumberTimes(x) { return Number($('#second').val()) * x; }
function getThirdNumberTimes(x) { return Number($('#third').val()) * x; }
app.js
function getFirstNumberTimes(x) { return getNumberTimes('#first', x); }
function getSecondNumberTimes(x) { return getNumberTimes('#second', x); }
function getThirdNumberTimes(x) { return getNumberTimes('#third', x); }
function getNumberTimes(id, x) {
return Number($(id).val()) * x;
}
app.js
function getTimesFn(id) {
return function(x) {
var val = $(id).val();
return Number(val) * x;
};
}
var getFirstNumberTimes = getTimesFn('#first');
var getSecondNumberTimes = getTimesFn('#second');
var getThirdNumberTimes = getTimesFn('#third');
SIDE-EFFECTS
When testing an object, I get side effects.Problem
Solution This is really just the problem of using globals.
JavaScript’s dynamic nature makes global objects
easy to implement. Avoid this and create Factory
Functions instead of global Modules.
app.js
var statefullThing = (function() {
var _name = "Alice";
function getName() { return _name; }
function setName(name) { _name = name; }
return { getName: getName, setName: setName };
})();
app-spec.js
describe("statefulThing", function() {
var subject = statefullThing;
it("has a settable name", function() {
subject.setName('Bob');
expect(subject.getName()).toBe('Bob');
});
it("has expected default name", function() {
expect(subject.getName()).toBe('Alice');
});
});
app.js
function statefuleThingFactory() {
var _name = "Alice";
function getName() { return _name; }
function setName(name) { _name = name; }
return { getName: getName, setName: setName };
};
var statefulThing = statefulThingFactory();
app-spec.js
describe("statefulThing", function() {
var subject;
beforeEach(function() {
subject = statefulThingFactory();
});
it("has a settable name", function() {
subject.setName('Bob');
expect(subject.getName()).toBe('Bob');
});
it("has expected default name", function() {
expect(subject.getName()).toBe('Alice');
});
});
PYRAMIDS OF DOOM
My AJAX calls are nested really deeply and I have
some timing issues with them.
Problem
Solution Use Promises to remove the nesting and force the
call order to what is needed.
app.js
$.get('orders.json', function(orders) {
var order = findOrder(orders);
updateDom(order);
$.get('items/' + order.id, function(items) {
var item = findItem(items);
updateDom(item);
$.get('details/' + item.id, function(details) {
udpateDom(details);
});
});
});
app.js
get('orders.json').then(function(orders) {
var order = findOrder(orders);
updateDom(order);
return get('items/' + order);
}).then(function(items) {
var item = findItem(items);
updateDom(item);
return get('details/' + item.id);
}).then(function(details) {
updateDom(details);
});
app.js
function get(url) {
var deferred = Q.defer();
$.get({
url : url,
success: function(data) {
return deferred.resolve(data);
},
error: function(xhr, status, error) {
return deferred.reject(error);
}
});
return deferred.promise();
}
get('orders.json').then(function(orders) {
var order = findOrder(orders);
updateDom(order);
return get('items/' + order);
}).then(function(items) {
var item = findItem(items);
updateDom(item);
return get('details/' + item.id);
}).then(function(details) {
updateDom(details);
});
NOT SURE IF I SHOULD ASK A
QUESTION
OR JUST RUN FOR THE DOOR
THANKS!
@guyroyse
guy@guyroyse.com
guyroyse.com

More Related Content

PDF
Frontin like-a-backer
PDF
Integrating React.js with PHP projects
PDF
Min-Maxing Software Costs - Laracon EU 2015
PDF
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2K
PDF
Nativescript angular
PDF
Graphql, REST and Apollo
PDF
My Top 5 APEX JavaScript API's
PDF
Presentation technico-commercial-ruby-on-rails
Frontin like-a-backer
Integrating React.js with PHP projects
Min-Maxing Software Costs - Laracon EU 2015
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2K
Nativescript angular
Graphql, REST and Apollo
My Top 5 APEX JavaScript API's
Presentation technico-commercial-ruby-on-rails

What's hot (20)

PPTX
AngularJS Animations
PPTX
Good karma: UX Patterns and Unit Testing in Angular with Karma
PDF
jQuery in 15 minutes
KEY
Javascript unit testing, yes we can e big
PDF
GDayX - Advanced Angular.JS
PDF
Tweaking the interactive grid
PDF
Advanced Django
PDF
Taming Command Bus
PPTX
AngularJS with TypeScript and Windows Azure Mobile Services
PDF
KISS Automation.py
PDF
Crossing platforms with JavaScript & React
PPTX
Jquery Complete Presentation along with Javascript Basics
PPTX
Angular mix chrisnoring
PDF
PHPUnit Episode iv.iii: Return of the tests
PDF
Hooks WCSD12
PPTX
Owl: The New Odoo UI Framework
PDF
Ultimate Introduction To AngularJS
PDF
Using Task Queues and D3.js to build an analytics product on App Engine
PDF
DOM Scripting Toolkit - jQuery
PDF
Be pragmatic, be SOLID (at Boiling Frogs, Wrocław)
AngularJS Animations
Good karma: UX Patterns and Unit Testing in Angular with Karma
jQuery in 15 minutes
Javascript unit testing, yes we can e big
GDayX - Advanced Angular.JS
Tweaking the interactive grid
Advanced Django
Taming Command Bus
AngularJS with TypeScript and Windows Azure Mobile Services
KISS Automation.py
Crossing platforms with JavaScript & React
Jquery Complete Presentation along with Javascript Basics
Angular mix chrisnoring
PHPUnit Episode iv.iii: Return of the tests
Hooks WCSD12
Owl: The New Odoo UI Framework
Ultimate Introduction To AngularJS
Using Task Queues and D3.js to build an analytics product on App Engine
DOM Scripting Toolkit - jQuery
Be pragmatic, be SOLID (at Boiling Frogs, Wrocław)
Ad

Viewers also liked (7)

PDF
Programação Web com jQuery
PPTX
Jquery introduction
PDF
jQueryのその先へ〜Webフロントエンドの全体感をつかもう〜
PPT
jQuery Learning
PPTX
A Power User's Intro to jQuery Awesomeness in SharePoint
PDF
The jQuery Divide
PDF
jQuery Introduction
Programação Web com jQuery
Jquery introduction
jQueryのその先へ〜Webフロントエンドの全体感をつかもう〜
jQuery Learning
A Power User's Intro to jQuery Awesomeness in SharePoint
The jQuery Divide
jQuery Introduction
Ad

Similar to jQuery & 10,000 Global Functions: Working with Legacy JavaScript (20)

PDF
Surviving javascript.pptx
PPTX
JS Essence
PPTX
Javascript best practices
PDF
Writing JavaScript that doesn't suck
PDF
379008-rc217-functionalprogramming
PPTX
In search of JavaScript code quality: unit testing
PDF
JAVASCRIPT Test Driven Development & Jasmine
PDF
Lessons from a coding veteran - Web Directions @Media
PDF
HTML5 for the Silverlight Guy
PDF
Good Coding Practices with JavaScript
PPTX
Advanced JavaScript
PPTX
TDD and the Legacy Code Black Hole
PDF
Refactoring JavaScript turning bad code into good code First Edition Burchard
PDF
Leveling Up at JavaScript
KEY
JavaScript Neednt Hurt - JavaBin talk
PDF
Refactoring Large Web Applications with Backbone.js
PDF
Refactor Large applications with Backbone
PDF
Refactor Large apps with Backbone
PPTX
Amin Milani Fard: Directed Model Inference for Testing and Analysis of Web Ap...
PDF
Building a JavaScript Library
Surviving javascript.pptx
JS Essence
Javascript best practices
Writing JavaScript that doesn't suck
379008-rc217-functionalprogramming
In search of JavaScript code quality: unit testing
JAVASCRIPT Test Driven Development & Jasmine
Lessons from a coding veteran - Web Directions @Media
HTML5 for the Silverlight Guy
Good Coding Practices with JavaScript
Advanced JavaScript
TDD and the Legacy Code Black Hole
Refactoring JavaScript turning bad code into good code First Edition Burchard
Leveling Up at JavaScript
JavaScript Neednt Hurt - JavaBin talk
Refactoring Large Web Applications with Backbone.js
Refactor Large applications with Backbone
Refactor Large apps with Backbone
Amin Milani Fard: Directed Model Inference for Testing and Analysis of Web Ap...
Building a JavaScript Library

More from Guy Royse (9)

PDF
Putting the D&D in TDD
PDF
Machine Learning for Fun - Finding Bigfoot with the Nexosis API
PDF
Machine Learning for Gamers - Dungeon Forecasts & Dragon Regressions
PPTX
The Code Christmas Tree: Selling the Investment for Technical Debt
PPTX
Mad Computer Science: Testing COBOL with RSpec
PPTX
Programming on Bare Metal: Controlling Circuits with Code
PPTX
Putting the D&D in TDD
PPTX
Those Who Know History are Doomed to Watch Others Repeat It
PPTX
Understanding Prototypal Inheritance
Putting the D&D in TDD
Machine Learning for Fun - Finding Bigfoot with the Nexosis API
Machine Learning for Gamers - Dungeon Forecasts & Dragon Regressions
The Code Christmas Tree: Selling the Investment for Technical Debt
Mad Computer Science: Testing COBOL with RSpec
Programming on Bare Metal: Controlling Circuits with Code
Putting the D&D in TDD
Those Who Know History are Doomed to Watch Others Repeat It
Understanding Prototypal Inheritance

Recently uploaded (20)

PDF
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
PPTX
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
PDF
Modernizing your data center with Dell and AMD
PPT
Teaching material agriculture food technology
PPTX
Cloud computing and distributed systems.
PPTX
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
PDF
CIFDAQ's Market Insight: SEC Turns Pro Crypto
PDF
Reach Out and Touch Someone: Haptics and Empathic Computing
PDF
Building Integrated photovoltaic BIPV_UPV.pdf
PPTX
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
PDF
Machine learning based COVID-19 study performance prediction
PDF
Agricultural_Statistics_at_a_Glance_2022_0.pdf
PPTX
Digital-Transformation-Roadmap-for-Companies.pptx
PDF
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
PDF
Chapter 3 Spatial Domain Image Processing.pdf
PDF
Spectral efficient network and resource selection model in 5G networks
PPTX
Big Data Technologies - Introduction.pptx
PDF
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
PDF
Network Security Unit 5.pdf for BCA BBA.
PDF
The Rise and Fall of 3GPP – Time for a Sabbatical?
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
Modernizing your data center with Dell and AMD
Teaching material agriculture food technology
Cloud computing and distributed systems.
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
CIFDAQ's Market Insight: SEC Turns Pro Crypto
Reach Out and Touch Someone: Haptics and Empathic Computing
Building Integrated photovoltaic BIPV_UPV.pdf
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
Machine learning based COVID-19 study performance prediction
Agricultural_Statistics_at_a_Glance_2022_0.pdf
Digital-Transformation-Roadmap-for-Companies.pptx
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
Chapter 3 Spatial Domain Image Processing.pdf
Spectral efficient network and resource selection model in 5G networks
Big Data Technologies - Introduction.pptx
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
Network Security Unit 5.pdf for BCA BBA.
The Rise and Fall of 3GPP – Time for a Sabbatical?

jQuery & 10,000 Global Functions: Working with Legacy JavaScript

  • 1. JQUERY & 10,000 GLOBAL FUNCTIONS Working with Legacy JavaScript
  • 6. Our Concern The Mapping Their Concern
  • 8. WHAT DOES THIS HAVE TO DO WITH JAVASCRIPT?
  • 10. <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0;"> <title>GuyRoyse.com | Blog</title> <link rel="icon" type="image/jpg" href="/images/alien-sunrise-icon.png"> <link rel="stylesheet" href=“/site.css"> <script> 💩</script> </head> <body> … </body> </html>
  • 11. SOLUTION #1: WE NEED A FRAMEWORK!
  • 12. SOLUTION #2: WE NEED A REWRITE! A new tiger team is selected. Everyone wants to be on this team because it’s a green-field project. They get to start over and create something truly beautiful. But only the best and brightest are chosen for the tiger team. Everyone else must continue to maintain the current system. Now the two teams are in a race. The tiger team must build a new system that does everything that the old system does. Not only that, they have to keep up with the changes that are continuously being made to the old system. Management will not replace the old system until the new system can do everything that the old system does. This race can go on for a very long time. I’ve seen it take 10 years. And by the time it’s done, the original members of the tiger team are long gone, and the current members are demanding that the new system be redesigned because it’s such a mess. Martin, Robert C. (2008-08-01). Clean Code: A Handbook of Agile Software Craftsmanship (p. 5). Pearson Education (USA). Kindle Edition.
  • 15. WHAT’S A 27-LETTER WORD FOR “CORN”?
  • 16. THE SIZZLE I have code in the HTML or in an associated JavaScript file that does some sort of fancy UI thing in a dated way. Problem Solution Refactor to use modern CSS and test manually. There is very little point in putting automated tests around visual effects.
  • 17. index.html <script> function addDropShadow(tag) { tag.style.backgroundColor = "black"; } function removeDropShadow(tag) { tag.style.backgroundColor = ""; } </script> <a onmouseenter="addDropShadow(this)" onmouseleave=“removeDropShadow(this)" href="somepage.jsp">Rollover Test</a> index.html <style> .rollover:hover { background-color: black; } </style> <a class="rollover">Rollover Test</a>
  • 18. CODE IN THE HTML I have code in the HTML. It cannot be loaded into a test harness to test it. Problem Solution Move code a .js file and add a script tag if needed. Surround code in question with a test if possible or extract a method and test that. If there is code in event attributes in the HTML, used jQuery to bind to those events instead.
  • 19. index.html <script> function doAwesomeThing() { thing.doAwesomeStuff("Awesome App is Awesome"); } </script> <a onclick="doAwesomeThing()">Awesome Thing</a> index.html <script src="awesome.js"></script> <a id="awesomeThing">Awesome Thing</a> awesome.js $('#awesomeThing').on('click', onAwesomeThingClicked); function onAwesomeThingClicked() { thing.doAwesomeStuff("Awesome App is Awesome"); } awesome-spec.js describe("awesome thing", function() { beforeEach(function() { spyOn(thing, 'doAwesomeStuff'); }); it("does awesome stuff when clicked", function() { onAwesomeThingClicked(); expect(thing.doAwesomeStuff).toHaveBeenCalledWith('Awesome App is Awesome'); }); });
  • 20. DOM INTERACTION A lot of the code interacts with the DOM and I don’t have a DOM in my unit test. Problem Solution You can have a DOM in your unit tests. Mock out HTML elements using Jasmine jQuery. Use jQuery to trigger events and to validate that the DOM is modified in the expected way.
  • 21. index.html <script src="awesome.js"></script> <a id="awesomeThing">Awesome Thing</a> <div id="toBeAwesome"/> awesome.js $('#awesomeThing').on('click', onAwesomeThingClicked); function onAwesomeThingClicked() { $('#toBeAwesome').html("Awesome App is Awesome"); } awesome-spec.js describe("awesome thing", function() { beforeEach(function() { setFixtures('<div id="toBeAwesome"/>'); }); it("does awesome stuff when clicked", function() { onAwesomeThingClicked(); expect($('#toBeAwesome')).toHaveHtml('Awesome App is Awesome'); }); });
  • 22. 10,000 GLOBAL FUNCTIONS I have functions everywhere, man!Problem Solution Put a test around the global function. Move the global function to a Module and replace the global function with a variable that points to the Module. Later, when no more code is accessing the global, remove the global variable.
  • 23. app.js function transmogrify() {}; function transmute() {}; function transform() {}; app.js var Mutators = {}; Mutators.transmogrify = function() {}; Mutators.transmute = function() {}; Mutators.transform = function() {}; var transmogrify = Mutators.transmogrify(); var transmute = Mutators.transmute(); var transform = Mutators.transform(); app-spec.js describe("Mutators.transform", function() { it("has mapping to legacy function", function() { expect(transform).toBe(Mutators.transform); }); it("does stuff", function() { expect(Mutators.transform()).toBe(true); }); });
  • 24. NO UNITS There are no units to test.Problem Solution Carefully extract a function and place tests around that function. Be careful not to create a 10,000 Global Functions problem.
  • 25. app.js $(function() { var id = $('#id').val(); $.get('data/' + id, function(data) { $('#name').val(data.name); $('#rank').val(data.rank); $('#serialNo').val(data.serial); }); }); app.js $(function() { fetchAndUpdateDom(); }); function fetchAndUpdateDom() { var id = $('#id').val(); $.get('data/' + id, function(data) { updateDom(data); }); }; function updateDom(data) { $('#name').val(data.name); $('#rank').val(data.rank); $('#serialNo').val(data.serial); }
  • 26. JQUERY EVERYWHERE I have jQuery calls inline everywhere in my code.Problem Solution Using the Module pattern, migrate the jQuery that interacts with the DOM into Passive Views. Migrate the jQuery that makes AJAX calls into Adapters. Place tests around these Modules.
  • 27. app.js function fetchAndUpdateDom() { var id = $('#id').val(); $.get('data/' + id, function(data) { $('#name').val(data.name); $('#rank').val(data.rank); $('#serialNo').val(data.serial); }); }; app.js var view = { id : function() { return $('#id').val(); }, name : function(val) { $('#name').val(val); }, rank : function(val) { $('#rank').val(val); }, serialNo : function(val) { $('#serialNo').val(val); } }; var data = { fetch : function(id, callback) { $.get('data/' + id, callback); } }; function fetchAndUpdateDom() { data.fetch(view.id(), function(data) { view.name(data.name); view.rank(data.rank); view.serialNo(data.serialNo); }); }
  • 28. REALLY UGLY FOR LOOPS These nested for loops are hideous!Problem Solution Use Underscore, lodash, or the built in array functions like forEach, find, reduce, and map.
  • 29. app.js var values = [1,2,3,4,5], sum = 0, evens = [], doubles = [], i; for(i = 0; i < values.length; i++) { sum += values[i] } for(i = 0; i < values.length; i++) { if (values[i] % 2 == 0) evens.push(values[i]); } for(i = 0; i < values.length; i++) { doubles.push(values[i] *2); } app.js var values = [1,2,3,4,5]; var sum = values.reduce(function(prev, value) { return prev + value; }, 0); var evens = values.filter(function(value) { return value % 2 === 0; }); var doubles = values.map(function(value) { return value * 2; });
  • 30. REALLY LONG FUNCTIONS This function is too long!Problem Solution Place a test around the function and refactor it to a Revealing Module.
  • 31. app.js function doMaths(x, y) { var add = x + y; var sub = x - y; var multi = x * y; var div = x / y; return { add: add, sub: sub, multi: multi, div: div }; }; app.js var mather = (function() { var _x, _y; function doMaths(x, y) { _x = x; _y = y; return { add: add(), sub: sub(), multi: multi(), div: div() }; } function add() { return _x + _y; } function sub() { return _x - _y; } function multi() { return _x * _y; } function div() { return _x / _y; } return { doMaths : doMaths }; })(); mather.doMaths(x, y);
  • 32. ALMOST DUPLICATION I have all these functions that do almost the same thing but I can’t break them down. Problem Solution Use a Lambda Factory to create the functions for you.
  • 33. app.js function getFirstNumberTimes(x) { return Number($('#first').val()) * x; } function getSecondNumberTimes(x) { return Number($('#second').val()) * x; } function getThirdNumberTimes(x) { return Number($('#third').val()) * x; } app.js function getFirstNumberTimes(x) { return getNumberTimes('#first', x); } function getSecondNumberTimes(x) { return getNumberTimes('#second', x); } function getThirdNumberTimes(x) { return getNumberTimes('#third', x); } function getNumberTimes(id, x) { return Number($(id).val()) * x; } app.js function getTimesFn(id) { return function(x) { var val = $(id).val(); return Number(val) * x; }; } var getFirstNumberTimes = getTimesFn('#first'); var getSecondNumberTimes = getTimesFn('#second'); var getThirdNumberTimes = getTimesFn('#third');
  • 34. SIDE-EFFECTS When testing an object, I get side effects.Problem Solution This is really just the problem of using globals. JavaScript’s dynamic nature makes global objects easy to implement. Avoid this and create Factory Functions instead of global Modules.
  • 35. app.js var statefullThing = (function() { var _name = "Alice"; function getName() { return _name; } function setName(name) { _name = name; } return { getName: getName, setName: setName }; })(); app-spec.js describe("statefulThing", function() { var subject = statefullThing; it("has a settable name", function() { subject.setName('Bob'); expect(subject.getName()).toBe('Bob'); }); it("has expected default name", function() { expect(subject.getName()).toBe('Alice'); }); }); app.js function statefuleThingFactory() { var _name = "Alice"; function getName() { return _name; } function setName(name) { _name = name; } return { getName: getName, setName: setName }; }; var statefulThing = statefulThingFactory(); app-spec.js describe("statefulThing", function() { var subject; beforeEach(function() { subject = statefulThingFactory(); }); it("has a settable name", function() { subject.setName('Bob'); expect(subject.getName()).toBe('Bob'); }); it("has expected default name", function() { expect(subject.getName()).toBe('Alice'); }); });
  • 36. PYRAMIDS OF DOOM My AJAX calls are nested really deeply and I have some timing issues with them. Problem Solution Use Promises to remove the nesting and force the call order to what is needed.
  • 37. app.js $.get('orders.json', function(orders) { var order = findOrder(orders); updateDom(order); $.get('items/' + order.id, function(items) { var item = findItem(items); updateDom(item); $.get('details/' + item.id, function(details) { udpateDom(details); }); }); }); app.js get('orders.json').then(function(orders) { var order = findOrder(orders); updateDom(order); return get('items/' + order); }).then(function(items) { var item = findItem(items); updateDom(item); return get('details/' + item.id); }).then(function(details) { updateDom(details); });
  • 38. app.js function get(url) { var deferred = Q.defer(); $.get({ url : url, success: function(data) { return deferred.resolve(data); }, error: function(xhr, status, error) { return deferred.reject(error); } }); return deferred.promise(); } get('orders.json').then(function(orders) { var order = findOrder(orders); updateDom(order); return get('items/' + order); }).then(function(items) { var item = findItem(items); updateDom(item); return get('details/' + item.id); }).then(function(details) { updateDom(details); });
  • 39. NOT SURE IF I SHOULD ASK A QUESTION OR JUST RUN FOR THE DOOR