javascript tdd with jasmine, karma, and gulp
TRANSCRIPT
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,
toBeUndefinedtoBeTruthy, toBeFalsytoContain, toBeGreaterThan, toBeLessThanCan 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/**/*.jsno exclusions
yes (watch for changes)
27 / 38
Run our first tests!
$ karma start17 09 2015 22:10:09.116:WARN [karma]: No captured browser, open http://localhost:17 09 2015 22:10:09.128:INFO [karma]: Karma v0.17 09 2015 22:10:09.132:INFO [launcher]: Starting browser PhantomJS17 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.jsvar 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 browser, open http://localhost:17 09 2015 21:37:44.087:INFO [karma]: Karma v0.17 09 2015 21:37:44.093:INFO [launcher]: Starting browser PhantomJS17 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