SlideShare a Scribd company logo
Testing Javascript from a
Frontend perspective
Frederic Cabassut - Campanda
The basics of test-driven development
TDD Cycle - Rule #1
You must write a failing test
before you write any production
code.
TDD Cycle - Rule #2
You must not write more of a
test than is sufficient to fail, or
fail to compile.
TDD Cycle - Rule #3
You must not write more
production code than is
sufficient to make the currently
failing test pass.
TDD Cycle
// tests // production code
TDD Cycle
// tests
var expected = "Hello, Fred!";
// production code
TDD Cycle
// tests
var expected = "Hello, Fred!";
// production code
✓
TDD Cycle
// tests
var expected = "Hello, Fred!";
var actual = getMyObj().greet("Fred");
// production code
TDD Cycle
// tests
var expected = "Hello, Fred!";
var actual = getMyObj().greet("Fred");
// production code
Uncaught ReferenceError: getMyObj is not defined
TDD Cycle
// tests
var expected = "Hello, Fred!";
var actual = getMyObj().greet("Fred");
// production code
function getMyObj() {
}
TDD Cycle
// tests
var expected = "Hello, Fred!";
var actual = getMyObj().greet("Fred");
// production code
function getMyObj() {
}
Uncaught TypeError: Cannot read property 'greet' of undefined
TDD Cycle
// tests
var expected = "Hello, Fred!";
var actual = getMyObj().greet("Fred");
// production code
function getMyObj() {
return {};
}
TDD Cycle
// tests
var expected = "Hello, Fred!";
var actual = getMyObj().greet("Fred");
// production code
function getMyObj() {
return {};
}
Uncaught TypeError: getMyObj(...).greet is not a function
TDD Cycle
// tests
var expected = "Hello, Fred!";
var actual = getMyObj().greet("Fred");
// production code
function getMyObj() {
return {
greet: function() {
}
};
}
}
TDD Cycle
// tests
var expected = "Hello, Fred!";
var actual = getMyObj().greet("Fred");
// production code
function getMyObj() {
return {
greet: function () {
}
};
}
✓
TDD Cycle
// tests
var expected = "Hello, Fred!";
var actual = getMyObj().greet("Fred");
assert.equal(expected, actual);
// production code
function getMyObj() {
return {
greet: function() {
}
};
}
TDD Cycle
// tests
var expected = "Hello, Fred!";
var actual = getMyObj().greet("Fred");
assert.equal(expected, actual);
// production code
function getMyObj() {
return {
greet: function() {
}
};
}
AssertionError: "Hello, Fred!" == undefined
TDD Cycle
// tests
var expected = "Hello, Fred!";
var actual = getMyObj().greet("Fred");
assert.equal(expected, actual);
// production code
function getMyObj() {
return {
greet: function() {
return "Hello, Fred!";
}
};
}
TDD Cycle
// tests
var expected = "Hello, Fred!";
var actual = getMyObj().greet("Fred");
assert.equal(expected, actual);
// production code
function getMyObj() {
return {
greet: function() {
return "Hello, Fred!";
}
};
}
✓
Why Testing ?
- Increase code quality
- Deploy with confidence
- Self-explaining documentation
- Easy to refactor
What do we have to test ?
- User interaction / events
- Manipulating the DOM
- Client-side business logic
- Ajax request (retrieve/send data)
My favorite testing stack:
- Mochajs https://mochajs.org/
- Chaijs http://chaijs.com/
- Sinonjs: http://sinonjs.org/
- My browser (+ livereload)
Let’s create a DatePicker!
Let’s create a DatePicker!
Let’s create a DatePicker!
Let’s create a DatePicker!
Let’s create a DatePicker!
User interaction / events
// tests
it('should initialize a datepicker with
a given element', function() {
var input =
document.createElement('input');
DatePicker.init(input);
});
// production code
window.DatePicker = {
init: function() {}
}
User interaction / events
// tests
it('should initialize a datepicker with
a given element', function() {
var input =
document.createElement('input');
DatePicker.init(input);
});
// production code
window.DatePicker = {
init: function() {}
}
✓
User interaction / events
// tests
it('should show a datepicker on input
focus', function() {
});
// production code
window.DatePicker = {
init: function() {}
}
User interaction / events
// tests
it('should show a datepicker on input
focus', function() {
var input =
document.createElement('input');
DatePicker.init(input);
});
// production code
window.DatePicker = {
init: function() {}
}
User interaction / events
// tests
it('should show a datepicker on input
focus', function() {
var input =
document.createElement('input');
DatePicker.init(input);
});
// production code
window.DatePicker = {
init: function() {}
}
✓
User interaction / events
// tests
it('should show a datepicker on input
focus', function() {
[...]
});
// production code
window.DatePicker = {
init: function() {}
}
User interaction / events
// tests
it('should show a datepicker on input
focus', function() {
[...]
var event = new Event('focus');
input.dispatchEvent(event);
});
// production code
window.DatePicker = {
init: function() {}
}
User interaction / events
// tests
it('should show a datepicker on input
focus', function() {
[...]
var event = new Event('focus');
input.dispatchEvent(event);
});
// production code
window.DatePicker = {
init: function() {}
}
✓
User interaction / events
// tests
it('should show a datepicker on input
focus', function() {
[...]
var event = new Event('focus');
input.dispatchEvent(event);
var element =
document.getElementById('datePicker');
expect(element).to.be.ok;
});
// production code
window.DatePicker = {
init: function() {}
}
User interaction / events
// tests
it('should show a datepicker on input
focus', function() {
[...]
var event = new Event('focus');
input.dispatchEvent(event);
var element =
document.getElementById('datePicker');
expect(element).to.be.ok;
});
// production code
window.DatePicker = {
init: function() {}
}
AssertionError: expected null to be truthy
User interaction / events
// tests
it('should show a datepicker on input
focus', function() {
[...]
var event = new Event('focus');
input.dispatchEvent(event);
var element =
document.getElementById('datePicker');
expect(element).to.be.ok;
});
// production code
window.DatePicker = {
init: function(input) {
[...]
}
}
User interaction / events
// tests
it('should show a datepicker on input
focus', function() {
[...]
var event = new Event('focus');
input.dispatchEvent(event);
var element =
document.getElementById('datePicker');
expect(element).to.be.ok;
});
// production code
[...]
input.addEventListener('focus',
function() {
var element =
document.createElement('div');
element.id = 'datePicker';
document.body.appendChild(element);
}
[...]
User interaction / events
// tests
it('should show a datepicker on input
focus', function() {
[...]
var event = new Event('focus');
input.dispatchEvent(event);
var element =
document.getElementById('datePicker');
expect(element).to.be.ok;
});
// production code
[...]
input.addEventListener('focus',
function() {
var element =
document.createElement('div');
element.id = 'datePicker';
document.body.appendChild(element);
}
[...]
✓
User interaction / events
// tests
it('should stop event propagation',
function() {
});
// production code
[...]
input.addEventListener('focus',
function() {
var element =
document.createElement('div');
element.id = 'datePicker';
document.body.appendChild(element);
}
[...]
User interaction / events
// tests
it('should stop event propagation',
function() {
[...]
var event = new Event('focus');
var spy = sinon.spy(event,
'stopPropagation');
input.dispatchEvent(event);
expect(spy).to.have.been.calledOnce;
});
// production code
[...]
input.addEventListener('focus',
function() {
var element =
document.createElement('div');
element.id = 'datePicker';
document.body.appendChild(element);
}
[...]
User interaction / events
// tests
it('should stop event propagation',
function() {
[...]
var event = new Event('focus');
var spy = sinon.spy(event,
'stopPropagation');
input.dispatchEvent(event);
expect(spy).to.have.been.calledOnce;
});
// production code
[...]
input.addEventListener('focus',
function() {
var element =
document.createElement('div');
element.id = 'datePicker';
document.body.appendChild(element);
}
[...]
expected stopPropagation to have been called exactly once ...
User interaction / events
// tests
it('should stop event propagation',
function() {
[...]
var event = new Event('focus');
var spy = sinon.spy(event,
'stopPropagation');
input.dispatchEvent(event);
expect(spy).to.have.been.calledOnce;
});
// production code
[...]
input.addEventListener('focus',
function(e) {
e.stopPropagation();
var element =
document.createElement('div');
element.id = 'datePicker';
document.body.appendChild(element);
}
[...]
User interaction / events
// tests
it('should stop event propagation',
function() {
[...]
var event = new Event('focus');
var spy = sinon.spy(event,
'stopPropagation');
input.dispatchEvent(event);
expect(spy).to.have.been.calledOnce;
});
// production code
[...]
input.addEventListener('focus',
function(e) {
e.stopPropagation();
var element =
document.createElement('div');
element.id = 'datePicker';
document.body.appendChild(element);
}
[...]
✓
Manipulating the DOM
// tests
it('should add class "selected" on a day when it is clicked',
function(){
});
Manipulating the DOM
// tests
it('should add class "selected" on a day when it is clicked',
function(){
[...]
var day = document.querySelector('#datePicker .day');
day.click();
});
Manipulating the DOM
// tests
it('adds class "selected" on a day when clicked', function() {
[...]
var day = document.querySelector('#datePicker .day');
day.click();
var i = day.className.indexOf('selected');
expect(i > -1).to.be.true;
});
Mocking Time, tick tick tick
// tests
Mocking Time
// tests
it('should hide datePicker after 300ms', function(){
[...]
var clock = sinon.useFakeTimers();
day.click();
clock.tick(299);
expect(datePicker.className.indexOf('hidden') > -1).to.be.false;
clock.tick(2);
expect(datePicker.className.indexOf('hidden') > -1).to.be.true;
clock.restore();
});
Mocking Browser methods
// tests
Mocking Browser methods
// tests
beforeEach(function() {
this.dateNowStub = sinon.stub(Date, 'now', function() {
return new Date('2016-01-13').getTime();
});
});
afterEach(function(){
this.dateNowStub.restore();
});
Testing Javascript for the Browser
In a browser
Take it to the next level
Headless browser testing (phantomjs, zombie, electron...)
Continuous Integration: automate your tests in jenkins, travis…
A few more tips
- Find a BUG ? Write a TEST!
- Do TDD while PAIRING
- Use the Web APIs in your TESTS
- Don’t try to test the browser
- You don’t need to test libraries
THANK YOU
And HAPPY Testing!

More Related Content

PDF
Painless JavaScript Testing with Jest
PDF
Unit Testing with Jest
PDF
Advanced Jasmine - Front-End JavaScript Unit Testing
PDF
Adventures In JavaScript Testing
PDF
JavaScript TDD with Jasmine and Karma
PDF
Introduction to Protractor
PDF
Intro to testing Javascript with jasmine
PDF
Testing JavaScript Applications
Painless JavaScript Testing with Jest
Unit Testing with Jest
Advanced Jasmine - Front-End JavaScript Unit Testing
Adventures In JavaScript Testing
JavaScript TDD with Jasmine and Karma
Introduction to Protractor
Intro to testing Javascript with jasmine
Testing JavaScript Applications

What's hot (20)

PDF
JavaScript Test-Driven Development with Jasmine 2.0 and Karma
KEY
Testing JS with Jasmine
PDF
AngularJS Unit Testing w/Karma and Jasmine
PDF
Quick tour to front end unit testing using jasmine
PDF
Jasmine - why JS tests don't smell fishy
PDF
Jasmine BDD for Javascript
PDF
Night Watch with QA
PDF
Intro to Unit Testing in AngularJS
PDF
We Are All Testers Now: The Testing Pyramid and Front-End Development
PPTX
Unit testing in JavaScript with Jasmine and Karma
ODP
Unit Testing and Coverage for AngularJS
PDF
Angular testing
PDF
Test-Driven Development of AngularJS Applications
PDF
Сергей Больщиков "Protractor Tips & Tricks"
PPT
Testing in AngularJS
PDF
Unit Testing JavaScript Applications
PDF
Javascript TDD with Jasmine, Karma, and Gulp
PDF
PL/SQL Unit Testing Can Be Fun!
PDF
Vuejs testing
PDF
AngularJS Unit Test
JavaScript Test-Driven Development with Jasmine 2.0 and Karma
Testing JS with Jasmine
AngularJS Unit Testing w/Karma and Jasmine
Quick tour to front end unit testing using jasmine
Jasmine - why JS tests don't smell fishy
Jasmine BDD for Javascript
Night Watch with QA
Intro to Unit Testing in AngularJS
We Are All Testers Now: The Testing Pyramid and Front-End Development
Unit testing in JavaScript with Jasmine and Karma
Unit Testing and Coverage for AngularJS
Angular testing
Test-Driven Development of AngularJS Applications
Сергей Больщиков "Protractor Tips & Tricks"
Testing in AngularJS
Unit Testing JavaScript Applications
Javascript TDD with Jasmine, Karma, and Gulp
PL/SQL Unit Testing Can Be Fun!
Vuejs testing
AngularJS Unit Test
Ad

Viewers also liked (13)

PDF
The Developer Experience
PDF
Unit Testing Lightning Components with Jasmine
PDF
Developer Experience to Testing
PPTX
JavaScript Unit Testing
PDF
Introducing Sencha Touch 2
PDF
How do I write Testable Javascript - Presented at dev.Objective() June 16, 2016
PDF
Javascript testing: tools of the trade
PDF
Javascript Unit Testing Tools
PDF
JAVASCRIPT Test Driven Development & Jasmine
PDF
Unit-testing and E2E testing in JS
PPTX
SenchaCon 2016: The Changing Landscape of JavaScript Testing - Joel Watson an...
PPTX
PPTX
Михаил Боднарчук "Acceptance Testing in NodeJS: Tools & Approaches"
The Developer Experience
Unit Testing Lightning Components with Jasmine
Developer Experience to Testing
JavaScript Unit Testing
Introducing Sencha Touch 2
How do I write Testable Javascript - Presented at dev.Objective() June 16, 2016
Javascript testing: tools of the trade
Javascript Unit Testing Tools
JAVASCRIPT Test Driven Development & Jasmine
Unit-testing and E2E testing in JS
SenchaCon 2016: The Changing Landscape of JavaScript Testing - Joel Watson an...
Михаил Боднарчук "Acceptance Testing in NodeJS: Tools & Approaches"
Ad

Similar to Testing javascript in the frontend (20)

PDF
Writing testable js [by Ted Piotrowski]
PDF
The Open Web and what it means
PPTX
AngularJS Internal
PPTX
AngularJS Architecture
ODP
Non Conventional Android Programming En
ODP
Non Conventional Android Programming (English)
PDF
Writing JavaScript that doesn't suck
PDF
Clean Javascript
PDF
The Beauty Of Java Script V5a
PDF
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
PDF
Building Grails Plugins - Tips And Tricks
PPTX
A Test of Strength
PDF
Desafios do Profissionalismo Ágil
PDF
Angular JS2 Training Session #2
PPTX
IndexedDB and Push Notifications in Progressive Web Apps
PDF
The Beauty of Java Script
PDF
Workshop 23: ReactJS, React & Redux testing
PDF
Carmen Popoviciu - Protractor styleguide | Codemotion Milan 2015
PDF
JavaScript APIs - The Web is the Platform
KEY
Workshop quality assurance for php projects tek12
Writing testable js [by Ted Piotrowski]
The Open Web and what it means
AngularJS Internal
AngularJS Architecture
Non Conventional Android Programming En
Non Conventional Android Programming (English)
Writing JavaScript that doesn't suck
Clean Javascript
The Beauty Of Java Script V5a
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
Building Grails Plugins - Tips And Tricks
A Test of Strength
Desafios do Profissionalismo Ágil
Angular JS2 Training Session #2
IndexedDB and Push Notifications in Progressive Web Apps
The Beauty of Java Script
Workshop 23: ReactJS, React & Redux testing
Carmen Popoviciu - Protractor styleguide | Codemotion Milan 2015
JavaScript APIs - The Web is the Platform
Workshop quality assurance for php projects tek12

Recently uploaded (20)

PDF
How to Choose the Right IT Partner for Your Business in Malaysia
PDF
System and Network Administration Chapter 2
PPTX
CHAPTER 2 - PM Management and IT Context
PDF
Internet Downloader Manager (IDM) Crack 6.42 Build 41
PPTX
Operating system designcfffgfgggggggvggggggggg
PPTX
Transform Your Business with a Software ERP System
PDF
top salesforce developer skills in 2025.pdf
PDF
Which alternative to Crystal Reports is best for small or large businesses.pdf
PPTX
CHAPTER 12 - CYBER SECURITY AND FUTURE SKILLS (1) (1).pptx
PPTX
Lecture 3: Operating Systems Introduction to Computer Hardware Systems
PDF
How to Migrate SBCGlobal Email to Yahoo Easily
PDF
Audit Checklist Design Aligning with ISO, IATF, and Industry Standards — Omne...
PPT
Introduction Database Management System for Course Database
PDF
Odoo Companies in India – Driving Business Transformation.pdf
PPTX
ManageIQ - Sprint 268 Review - Slide Deck
PPTX
Agentic AI : A Practical Guide. Undersating, Implementing and Scaling Autono...
PDF
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
PDF
2025 Textile ERP Trends: SAP, Odoo & Oracle
PDF
Adobe Illustrator 28.6 Crack My Vision of Vector Design
PDF
Flood Susceptibility Mapping Using Image-Based 2D-CNN Deep Learnin. Overview ...
How to Choose the Right IT Partner for Your Business in Malaysia
System and Network Administration Chapter 2
CHAPTER 2 - PM Management and IT Context
Internet Downloader Manager (IDM) Crack 6.42 Build 41
Operating system designcfffgfgggggggvggggggggg
Transform Your Business with a Software ERP System
top salesforce developer skills in 2025.pdf
Which alternative to Crystal Reports is best for small or large businesses.pdf
CHAPTER 12 - CYBER SECURITY AND FUTURE SKILLS (1) (1).pptx
Lecture 3: Operating Systems Introduction to Computer Hardware Systems
How to Migrate SBCGlobal Email to Yahoo Easily
Audit Checklist Design Aligning with ISO, IATF, and Industry Standards — Omne...
Introduction Database Management System for Course Database
Odoo Companies in India – Driving Business Transformation.pdf
ManageIQ - Sprint 268 Review - Slide Deck
Agentic AI : A Practical Guide. Undersating, Implementing and Scaling Autono...
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
2025 Textile ERP Trends: SAP, Odoo & Oracle
Adobe Illustrator 28.6 Crack My Vision of Vector Design
Flood Susceptibility Mapping Using Image-Based 2D-CNN Deep Learnin. Overview ...

Testing javascript in the frontend

  • 1. Testing Javascript from a Frontend perspective Frederic Cabassut - Campanda
  • 2. The basics of test-driven development
  • 3. TDD Cycle - Rule #1 You must write a failing test before you write any production code.
  • 4. TDD Cycle - Rule #2 You must not write more of a test than is sufficient to fail, or fail to compile.
  • 5. TDD Cycle - Rule #3 You must not write more production code than is sufficient to make the currently failing test pass.
  • 6. TDD Cycle // tests // production code
  • 7. TDD Cycle // tests var expected = "Hello, Fred!"; // production code
  • 8. TDD Cycle // tests var expected = "Hello, Fred!"; // production code ✓
  • 9. TDD Cycle // tests var expected = "Hello, Fred!"; var actual = getMyObj().greet("Fred"); // production code
  • 10. TDD Cycle // tests var expected = "Hello, Fred!"; var actual = getMyObj().greet("Fred"); // production code Uncaught ReferenceError: getMyObj is not defined
  • 11. TDD Cycle // tests var expected = "Hello, Fred!"; var actual = getMyObj().greet("Fred"); // production code function getMyObj() { }
  • 12. TDD Cycle // tests var expected = "Hello, Fred!"; var actual = getMyObj().greet("Fred"); // production code function getMyObj() { } Uncaught TypeError: Cannot read property 'greet' of undefined
  • 13. TDD Cycle // tests var expected = "Hello, Fred!"; var actual = getMyObj().greet("Fred"); // production code function getMyObj() { return {}; }
  • 14. TDD Cycle // tests var expected = "Hello, Fred!"; var actual = getMyObj().greet("Fred"); // production code function getMyObj() { return {}; } Uncaught TypeError: getMyObj(...).greet is not a function
  • 15. TDD Cycle // tests var expected = "Hello, Fred!"; var actual = getMyObj().greet("Fred"); // production code function getMyObj() { return { greet: function() { } }; } }
  • 16. TDD Cycle // tests var expected = "Hello, Fred!"; var actual = getMyObj().greet("Fred"); // production code function getMyObj() { return { greet: function () { } }; } ✓
  • 17. TDD Cycle // tests var expected = "Hello, Fred!"; var actual = getMyObj().greet("Fred"); assert.equal(expected, actual); // production code function getMyObj() { return { greet: function() { } }; }
  • 18. TDD Cycle // tests var expected = "Hello, Fred!"; var actual = getMyObj().greet("Fred"); assert.equal(expected, actual); // production code function getMyObj() { return { greet: function() { } }; } AssertionError: "Hello, Fred!" == undefined
  • 19. TDD Cycle // tests var expected = "Hello, Fred!"; var actual = getMyObj().greet("Fred"); assert.equal(expected, actual); // production code function getMyObj() { return { greet: function() { return "Hello, Fred!"; } }; }
  • 20. TDD Cycle // tests var expected = "Hello, Fred!"; var actual = getMyObj().greet("Fred"); assert.equal(expected, actual); // production code function getMyObj() { return { greet: function() { return "Hello, Fred!"; } }; } ✓
  • 21. Why Testing ? - Increase code quality - Deploy with confidence - Self-explaining documentation - Easy to refactor
  • 22. What do we have to test ? - User interaction / events - Manipulating the DOM - Client-side business logic - Ajax request (retrieve/send data)
  • 23. My favorite testing stack: - Mochajs https://mochajs.org/ - Chaijs http://chaijs.com/ - Sinonjs: http://sinonjs.org/ - My browser (+ livereload)
  • 24. Let’s create a DatePicker!
  • 25. Let’s create a DatePicker!
  • 26. Let’s create a DatePicker!
  • 27. Let’s create a DatePicker!
  • 28. Let’s create a DatePicker!
  • 29. User interaction / events // tests it('should initialize a datepicker with a given element', function() { var input = document.createElement('input'); DatePicker.init(input); }); // production code window.DatePicker = { init: function() {} }
  • 30. User interaction / events // tests it('should initialize a datepicker with a given element', function() { var input = document.createElement('input'); DatePicker.init(input); }); // production code window.DatePicker = { init: function() {} } ✓
  • 31. User interaction / events // tests it('should show a datepicker on input focus', function() { }); // production code window.DatePicker = { init: function() {} }
  • 32. User interaction / events // tests it('should show a datepicker on input focus', function() { var input = document.createElement('input'); DatePicker.init(input); }); // production code window.DatePicker = { init: function() {} }
  • 33. User interaction / events // tests it('should show a datepicker on input focus', function() { var input = document.createElement('input'); DatePicker.init(input); }); // production code window.DatePicker = { init: function() {} } ✓
  • 34. User interaction / events // tests it('should show a datepicker on input focus', function() { [...] }); // production code window.DatePicker = { init: function() {} }
  • 35. User interaction / events // tests it('should show a datepicker on input focus', function() { [...] var event = new Event('focus'); input.dispatchEvent(event); }); // production code window.DatePicker = { init: function() {} }
  • 36. User interaction / events // tests it('should show a datepicker on input focus', function() { [...] var event = new Event('focus'); input.dispatchEvent(event); }); // production code window.DatePicker = { init: function() {} } ✓
  • 37. User interaction / events // tests it('should show a datepicker on input focus', function() { [...] var event = new Event('focus'); input.dispatchEvent(event); var element = document.getElementById('datePicker'); expect(element).to.be.ok; }); // production code window.DatePicker = { init: function() {} }
  • 38. User interaction / events // tests it('should show a datepicker on input focus', function() { [...] var event = new Event('focus'); input.dispatchEvent(event); var element = document.getElementById('datePicker'); expect(element).to.be.ok; }); // production code window.DatePicker = { init: function() {} } AssertionError: expected null to be truthy
  • 39. User interaction / events // tests it('should show a datepicker on input focus', function() { [...] var event = new Event('focus'); input.dispatchEvent(event); var element = document.getElementById('datePicker'); expect(element).to.be.ok; }); // production code window.DatePicker = { init: function(input) { [...] } }
  • 40. User interaction / events // tests it('should show a datepicker on input focus', function() { [...] var event = new Event('focus'); input.dispatchEvent(event); var element = document.getElementById('datePicker'); expect(element).to.be.ok; }); // production code [...] input.addEventListener('focus', function() { var element = document.createElement('div'); element.id = 'datePicker'; document.body.appendChild(element); } [...]
  • 41. User interaction / events // tests it('should show a datepicker on input focus', function() { [...] var event = new Event('focus'); input.dispatchEvent(event); var element = document.getElementById('datePicker'); expect(element).to.be.ok; }); // production code [...] input.addEventListener('focus', function() { var element = document.createElement('div'); element.id = 'datePicker'; document.body.appendChild(element); } [...] ✓
  • 42. User interaction / events // tests it('should stop event propagation', function() { }); // production code [...] input.addEventListener('focus', function() { var element = document.createElement('div'); element.id = 'datePicker'; document.body.appendChild(element); } [...]
  • 43. User interaction / events // tests it('should stop event propagation', function() { [...] var event = new Event('focus'); var spy = sinon.spy(event, 'stopPropagation'); input.dispatchEvent(event); expect(spy).to.have.been.calledOnce; }); // production code [...] input.addEventListener('focus', function() { var element = document.createElement('div'); element.id = 'datePicker'; document.body.appendChild(element); } [...]
  • 44. User interaction / events // tests it('should stop event propagation', function() { [...] var event = new Event('focus'); var spy = sinon.spy(event, 'stopPropagation'); input.dispatchEvent(event); expect(spy).to.have.been.calledOnce; }); // production code [...] input.addEventListener('focus', function() { var element = document.createElement('div'); element.id = 'datePicker'; document.body.appendChild(element); } [...] expected stopPropagation to have been called exactly once ...
  • 45. User interaction / events // tests it('should stop event propagation', function() { [...] var event = new Event('focus'); var spy = sinon.spy(event, 'stopPropagation'); input.dispatchEvent(event); expect(spy).to.have.been.calledOnce; }); // production code [...] input.addEventListener('focus', function(e) { e.stopPropagation(); var element = document.createElement('div'); element.id = 'datePicker'; document.body.appendChild(element); } [...]
  • 46. User interaction / events // tests it('should stop event propagation', function() { [...] var event = new Event('focus'); var spy = sinon.spy(event, 'stopPropagation'); input.dispatchEvent(event); expect(spy).to.have.been.calledOnce; }); // production code [...] input.addEventListener('focus', function(e) { e.stopPropagation(); var element = document.createElement('div'); element.id = 'datePicker'; document.body.appendChild(element); } [...] ✓
  • 47. Manipulating the DOM // tests it('should add class "selected" on a day when it is clicked', function(){ });
  • 48. Manipulating the DOM // tests it('should add class "selected" on a day when it is clicked', function(){ [...] var day = document.querySelector('#datePicker .day'); day.click(); });
  • 49. Manipulating the DOM // tests it('adds class "selected" on a day when clicked', function() { [...] var day = document.querySelector('#datePicker .day'); day.click(); var i = day.className.indexOf('selected'); expect(i > -1).to.be.true; });
  • 50. Mocking Time, tick tick tick // tests
  • 51. Mocking Time // tests it('should hide datePicker after 300ms', function(){ [...] var clock = sinon.useFakeTimers(); day.click(); clock.tick(299); expect(datePicker.className.indexOf('hidden') > -1).to.be.false; clock.tick(2); expect(datePicker.className.indexOf('hidden') > -1).to.be.true; clock.restore(); });
  • 53. Mocking Browser methods // tests beforeEach(function() { this.dateNowStub = sinon.stub(Date, 'now', function() { return new Date('2016-01-13').getTime(); }); }); afterEach(function(){ this.dateNowStub.restore(); });
  • 54. Testing Javascript for the Browser In a browser
  • 55. Take it to the next level Headless browser testing (phantomjs, zombie, electron...) Continuous Integration: automate your tests in jenkins, travis…
  • 56. A few more tips - Find a BUG ? Write a TEST! - Do TDD while PAIRING - Use the Web APIs in your TESTS - Don’t try to test the browser - You don’t need to test libraries