KEMBAR78
Javascript TDD with Jasmine, Karma, and Gulp | PDF
JavaScript TDD with
Jasmine, Karma, and Gulp
All Things Open 2015
Jason Krol
1 / 38
About Me
Jason Krol
! " ShortTompkins
# Kroltech.com
Currently working at:
Dad, Author, Gamer, Geek!
http://ShortTompkins.JS.org
2 / 38
Why should we test?
Google is 2 Billion lines of code!1
Acts as a kind of guarantee
Built-in code documentation!
Testing can be fun?!
3 / 38
Why we probably aren't
Roughly 50% of FE devs aren't testing2
Its time consuming
It can be tedious and difficult
Where to start?!
4 / 38
Tools of the Trade
Jasmine (test/assertion library/framework)
Others: Mocha, Chai, QUnit, et al
Karma (test runner)
Gulp (task runner)
Others: Grunt, Brocolli, npm, make, et al
Requires node.js and npm installed locally
5 / 38
Test Driven Development Crash Course
6 / 38
Test Driven Development Crash Course
Suites, specs, assertions, spies, mocks, stubs,
wtf?!
7 / 38
Test Driven Development Crash Course
Suites, specs, assertions, spies, mocks, stubs,
wtf?!
Describe a piece of code / functionality
being tested
8 / 38
Test Driven Development Crash Course
Suites, specs, assertions, spies, mocks, stubs,
wtf?!
Describe a piece of code / functionality
being tested
Define setup work before and/or after
every test
9 / 38
Test Driven Development Crash Course
Suites, specs, assertions, spies, mocks, stubs,
wtf?!
Describe a piece of code / functionality
being tested
Define setup work before and/or after
every test
It should do exactly what you expect (all
possible scenarios!)
10 / 38
Test Driven Development Crash Course
Suites, specs, assertions, spies, mocks, stubs,
wtf?!
Describe a piece of code / functionality
being tested
Define setup work before and/or after
every test
It should do exactly what you expect (all
possible scenarios!)
Thats it!
11 / 38
Describe
describe('Description/Label', function(){
});
Typically a single word description/label
Can be nested (and very well should be)
12 / 38
beforeEach/afterEach
describe('Description/Label', function(){
beforeEach(function(){
// do some work before every test
});
afterEach(function(){
// do some cleanup/resets after
});
});
13 / 38
it
describe('Description/Label', function(){
beforeEach(function(){ ... });
it('should do something...', function(){
});
it('should also...', function(){
});
});
14 / 38
expect & matchers
describe('Sum', function(){
it('should sum 2 numbers', function(){
expect(sum(1, 1)).toEqual(2);
expect(sum(3, 2)).toEqual(5);
expect(sum(5, 5)).not.toEqual(99);
});
});
15 / 38
Some common matchers:
toBe, toEqual, toMatch, toBeDefined,
toBeUndefined
toBeTruthy, toBeFalsy
toContain, toBeGreaterThan, toBeLessThan
Can be chained with .not
not.toBeDefined(), not.toEqual(0)
Specials:
jasmine.any([Function, Number, String])
jasmine.anything()
16 / 38
Spies
17 / 38
Spies
spyOn(object, 'functionToSpyOn')
jasmine.createSpy('nameOfSpy')
Control spy's behavior:
.and.callThrough()
.and.returnValue(newValue)
.and.callFake(function(){ ... })
Matchers:
.toHaveBeenCalled(),
.toHaveBeenCalledWith(params)
18 / 38
spyOn
describe('Sum', function(){
beforeEach(function() {
spyOn(window, 'alert');
});
it('should alert the sum', function (){
sum(1, 1);
expect(window.alert)
.toHaveBeenCalledWith(2);
});
});
19 / 38
jasmine.createSpy
describe('window.setTimeout', function(){
var cbSpy;
beforeEach(function() {
cbSpy = jasmine.createSpy('cbSpy');
setTimeout(cbSpy, 0);
});
it('should execute callback', function (){
expect(cbSpy).toHaveBeenCalled();
expect(cbSpy).calls.count()).toBe(1);
});
});
20 / 38
Create a quick project
$ mkdir -p jstdd/src/js && cd jstdd
$ touch src/js/sample.js
$ touch src/js/sample_tests.js
21 / 38
File contents:
// sample.js:
function sum(a, b) {
window.alert(a + b);
return a + b;
}
22 / 38
// sample_tests.js:
describe('Sample', function(){
beforeEach(function () {
spyOn(window, 'alert');
});
it('should sum 2 numbers', function(){
expect(sum(1,1)).toEqual(2);
});
it('should alert the value', function(){
sum(2, 2);
expect(window.alert)
.toHaveBeenCalledWith(4);
});
});
23 / 38
Setup our Dev Environment
First, install Karma's CLI as a global package:
$ sudo npm install -g karma-cli phantomjs
24 / 38
Setup our Dev Environment
First, install Karma's CLI as a global package:
$ sudo npm install -g karma-cli phantomjs
Initialize our project:
$ npm init -y
25 / 38
Setup our Dev Environment
First, install Karma's CLI as a global package:
$ sudo npm install -g karma-cli phantomjs
Initialize our project:
$ npm init -y
Locally install karma for the project:
$ npm install --save-dev karma
26 / 38
Initialize Karma for the project:
$ karma init
jasmine
no (Require.js)
PhantomJS
src/js/**/*.js
no exclusions
yes (watch for changes)
27 / 38
Run our first tests!
$ karma start
17 09 2015 22:10:09.116:WARN [karma]: No captured browse
17 09 2015 22:10:09.128:INFO [karma]: Karma v0.
17 09 2015 22:10:09.132:INFO [launcher]: Starting browse
17 09 2015 22:10:10.064:INFO [PhantomJS 1.9.
PhantomJS 1.9.8: Executed 2 of 2 SUCCESS (0.003
|
28 / 38
Introducing Gulp
Why do we need an automated build tool?
29 / 38
Introducing Gulp
Why do we need an automated build tool?
Bundle/Minify our source code
Transpile CSS preprocessors like SASS
ES6 Transpiling (Babel)
Run tests!!
30 / 38
Install Gulp
First, like Karma, globally install the Gulp CLI:
$ sudo npm install -g gulp
31 / 38
Install Gulp
First, like Karma, globally install the Gulp CLI:
$ sudo npm install -g gulp
Locally install gulp and the gulp-karma plugin
for the project:
$ npm install --save-dev gulp gulp-karma
32 / 38
Install Gulp
First, like Karma, globally install the Gulp CLI:
$ sudo npm install -g gulp
Locally install gulp and the gulp-karma plugin
for the project:
$ npm install --save-dev gulp gulp-karma
Create a gulpfile.js:
$ touch gulpfile.js
33 / 38
Gulpfile.js
// gulpfile.js
var gulp = require('gulp'),
karma = require('gulp-karma');
gulp.task('default', function() {
gulp.src(['src/js/**/*.js'])
.pipe(karma({
configFile: 'karma.conf.js',
action: 'watch'
}));
});
34 / 38
Run our first Gulp test task!
$ gulp
[21:37:43] Using gulpfile ~/repos/jstdd/gulpfile.js
[21:37:43] Starting 'default'...
[21:37:43] Finished 'default' after 8.52 ms
[21:37:43] Starting Karma server...
17 09 2015 21:37:44.077:WARN [karma]: No captured browse
17 09 2015 21:37:44.087:INFO [karma]: Karma v0.
17 09 2015 21:37:44.093:INFO [launcher]: Starting browse
17 09 2015 21:37:45.044:INFO [PhantomJS 1.9.
PhantomJS 1.9.8: Executed 2 of 2 SUCCESS (0.003
|
35 / 38
Lets look at some real tests!
VideoPlayer.js & VideoPlayer_tests.js
Basic video player module
Uses jQuery
12 functions (100 lines)
46 tests (over 300 lines)
36 / 38
Resources
Jasmine Docs: http://jasmine.github.io/
Karma: http://karma-runner.github.io/
Gulp: http://gulpjs.com/
Sources
1. http://www.wired.com/2015/09/google-2-
billion-lines-codeand-one-place/
2. http://ashleynolan.co.uk/blog/frontend-
tooling-survey-2015-results
37 / 38
Start Testing!!
38 / 38

Javascript TDD with Jasmine, Karma, and Gulp

  • 1.
    JavaScript TDD with Jasmine,Karma, and Gulp All Things Open 2015 Jason Krol 1 / 38
  • 2.
    About Me Jason Krol !" ShortTompkins # Kroltech.com Currently working at: Dad, Author, Gamer, Geek! http://ShortTompkins.JS.org 2 / 38
  • 3.
    Why should wetest? Google is 2 Billion lines of code!1 Acts as a kind of guarantee Built-in code documentation! Testing can be fun?! 3 / 38
  • 4.
    Why we probablyaren't Roughly 50% of FE devs aren't testing2 Its time consuming It can be tedious and difficult Where to start?! 4 / 38
  • 5.
    Tools of theTrade Jasmine (test/assertion library/framework) Others: Mocha, Chai, QUnit, et al Karma (test runner) Gulp (task runner) Others: Grunt, Brocolli, npm, make, et al Requires node.js and npm installed locally 5 / 38
  • 6.
    Test Driven DevelopmentCrash Course 6 / 38
  • 7.
    Test Driven DevelopmentCrash Course Suites, specs, assertions, spies, mocks, stubs, wtf?! 7 / 38
  • 8.
    Test Driven DevelopmentCrash Course Suites, specs, assertions, spies, mocks, stubs, wtf?! Describe a piece of code / functionality being tested 8 / 38
  • 9.
    Test Driven DevelopmentCrash Course Suites, specs, assertions, spies, mocks, stubs, wtf?! Describe a piece of code / functionality being tested Define setup work before and/or after every test 9 / 38
  • 10.
    Test Driven DevelopmentCrash Course Suites, specs, assertions, spies, mocks, stubs, wtf?! Describe a piece of code / functionality being tested Define setup work before and/or after every test It should do exactly what you expect (all possible scenarios!) 10 / 38
  • 11.
    Test Driven DevelopmentCrash Course Suites, specs, assertions, spies, mocks, stubs, wtf?! Describe a piece of code / functionality being tested Define setup work before and/or after every test It should do exactly what you expect (all possible scenarios!) Thats it! 11 / 38
  • 12.
    Describe describe('Description/Label', function(){ }); Typically asingle word description/label Can be nested (and very well should be) 12 / 38
  • 13.
    beforeEach/afterEach describe('Description/Label', function(){ beforeEach(function(){ // dosome work before every test }); afterEach(function(){ // do some cleanup/resets after }); }); 13 / 38
  • 14.
    it describe('Description/Label', function(){ beforeEach(function(){ ...}); it('should do something...', function(){ }); it('should also...', function(){ }); }); 14 / 38
  • 15.
    expect & matchers describe('Sum',function(){ it('should sum 2 numbers', function(){ expect(sum(1, 1)).toEqual(2); expect(sum(3, 2)).toEqual(5); expect(sum(5, 5)).not.toEqual(99); }); }); 15 / 38
  • 16.
    Some common matchers: toBe,toEqual, toMatch, toBeDefined, toBeUndefined toBeTruthy, toBeFalsy toContain, toBeGreaterThan, toBeLessThan Can be chained with .not not.toBeDefined(), not.toEqual(0) Specials: jasmine.any([Function, Number, String]) jasmine.anything() 16 / 38
  • 17.
  • 18.
    Spies spyOn(object, 'functionToSpyOn') jasmine.createSpy('nameOfSpy') Control spy'sbehavior: .and.callThrough() .and.returnValue(newValue) .and.callFake(function(){ ... }) Matchers: .toHaveBeenCalled(), .toHaveBeenCalledWith(params) 18 / 38
  • 19.
    spyOn describe('Sum', function(){ beforeEach(function() { spyOn(window,'alert'); }); it('should alert the sum', function (){ sum(1, 1); expect(window.alert) .toHaveBeenCalledWith(2); }); }); 19 / 38
  • 20.
    jasmine.createSpy describe('window.setTimeout', function(){ var cbSpy; beforeEach(function(){ cbSpy = jasmine.createSpy('cbSpy'); setTimeout(cbSpy, 0); }); it('should execute callback', function (){ expect(cbSpy).toHaveBeenCalled(); expect(cbSpy).calls.count()).toBe(1); }); }); 20 / 38
  • 21.
    Create a quickproject $ mkdir -p jstdd/src/js && cd jstdd $ touch src/js/sample.js $ touch src/js/sample_tests.js 21 / 38
  • 22.
    File contents: // sample.js: functionsum(a, b) { window.alert(a + b); return a + b; } 22 / 38
  • 23.
    // sample_tests.js: describe('Sample', function(){ beforeEach(function() { spyOn(window, 'alert'); }); it('should sum 2 numbers', function(){ expect(sum(1,1)).toEqual(2); }); it('should alert the value', function(){ sum(2, 2); expect(window.alert) .toHaveBeenCalledWith(4); }); }); 23 / 38
  • 24.
    Setup our DevEnvironment First, install Karma's CLI as a global package: $ sudo npm install -g karma-cli phantomjs 24 / 38
  • 25.
    Setup our DevEnvironment First, install Karma's CLI as a global package: $ sudo npm install -g karma-cli phantomjs Initialize our project: $ npm init -y 25 / 38
  • 26.
    Setup our DevEnvironment First, install Karma's CLI as a global package: $ sudo npm install -g karma-cli phantomjs Initialize our project: $ npm init -y Locally install karma for the project: $ npm install --save-dev karma 26 / 38
  • 27.
    Initialize Karma forthe project: $ karma init jasmine no (Require.js) PhantomJS src/js/**/*.js no exclusions yes (watch for changes) 27 / 38
  • 28.
    Run our firsttests! $ karma start 17 09 2015 22:10:09.116:WARN [karma]: No captured browse 17 09 2015 22:10:09.128:INFO [karma]: Karma v0. 17 09 2015 22:10:09.132:INFO [launcher]: Starting browse 17 09 2015 22:10:10.064:INFO [PhantomJS 1.9. PhantomJS 1.9.8: Executed 2 of 2 SUCCESS (0.003 | 28 / 38
  • 29.
    Introducing Gulp Why dowe need an automated build tool? 29 / 38
  • 30.
    Introducing Gulp Why dowe need an automated build tool? Bundle/Minify our source code Transpile CSS preprocessors like SASS ES6 Transpiling (Babel) Run tests!! 30 / 38
  • 31.
    Install Gulp First, likeKarma, globally install the Gulp CLI: $ sudo npm install -g gulp 31 / 38
  • 32.
    Install Gulp First, likeKarma, globally install the Gulp CLI: $ sudo npm install -g gulp Locally install gulp and the gulp-karma plugin for the project: $ npm install --save-dev gulp gulp-karma 32 / 38
  • 33.
    Install Gulp First, likeKarma, globally install the Gulp CLI: $ sudo npm install -g gulp Locally install gulp and the gulp-karma plugin for the project: $ npm install --save-dev gulp gulp-karma Create a gulpfile.js: $ touch gulpfile.js 33 / 38
  • 34.
    Gulpfile.js // gulpfile.js var gulp= require('gulp'), karma = require('gulp-karma'); gulp.task('default', function() { gulp.src(['src/js/**/*.js']) .pipe(karma({ configFile: 'karma.conf.js', action: 'watch' })); }); 34 / 38
  • 35.
    Run our firstGulp test task! $ gulp [21:37:43] Using gulpfile ~/repos/jstdd/gulpfile.js [21:37:43] Starting 'default'... [21:37:43] Finished 'default' after 8.52 ms [21:37:43] Starting Karma server... 17 09 2015 21:37:44.077:WARN [karma]: No captured browse 17 09 2015 21:37:44.087:INFO [karma]: Karma v0. 17 09 2015 21:37:44.093:INFO [launcher]: Starting browse 17 09 2015 21:37:45.044:INFO [PhantomJS 1.9. PhantomJS 1.9.8: Executed 2 of 2 SUCCESS (0.003 | 35 / 38
  • 36.
    Lets look atsome real tests! VideoPlayer.js & VideoPlayer_tests.js Basic video player module Uses jQuery 12 functions (100 lines) 46 tests (over 300 lines) 36 / 38
  • 37.
    Resources Jasmine Docs: http://jasmine.github.io/ Karma:http://karma-runner.github.io/ Gulp: http://gulpjs.com/ Sources 1. http://www.wired.com/2015/09/google-2- billion-lines-codeand-one-place/ 2. http://ashleynolan.co.uk/blog/frontend- tooling-survey-2015-results 37 / 38
  • 38.