how to write testable javascript
TRANSCRIPT
★How do I writeTestable Javascript?
★Agenda★Who Am I?
★State of the Room?
★Ways to test Javascript?
★Different Testing Environments?
★Overview of Testing Tools
★Using Testing in your Workflow
★Spaghetti Javascript
★Refactor Spaghetti into Testable Javascript
★Installing Jasmine + Live Demo
★Who Am I?★Gavin Pickin – developing Web Apps since late 90s
○Ortus Solutions Software Consultant
○ContentBox Evangelist
★What else do you need to know?
○CFMLRepo.com http://www.cfmlrepo.com
○Blog - http://www.gpickin.com
○Twitter – http://twitter.com/gpickin
○Github - https://github.com/gpickin
★Lets get on with the show.
★State of the Room
★ A few questions for you guys★ If you have arms, use them.
★State of the RoomTesting? What’s testing?
★State of the Room
Yeah,I’ve heard of it.
Why do you think I’m here?
★State of the RoomYes I know I should be testing, but I’m not sure how to do it
★State of the RoomMy Boss and my Customers wouldn’t let me
★State of the Room
I’m a tester
★State of the RoomI’m a test writing ninjaCall me Majano, Luis Majano
★Ways to Test your Code★Click around in
the browser yourself
★Setup Selenium / Web Driver to click around for you
★Structured Programmatic Tests
★Types of Testing
★Types of Testing
★Black/White Box
★Unit Testing
★Integration Testing
★Functional Tests
★System Tests
★End to End Tests
★Sanity Testing
★Regression Test
★Acceptance Tests
★Load Testing
★Stress Test
★Performance Tests
★Usability Tests
★+ More
★Levels of Testing
★Cost of a Bug
The bug will cost one way or another
★Integration Testing
★Integration Testing
★Integration Tests several of the pieces together
★Most of the types of tests are variations of an Integration Test
★Can include mocks but can full end to end tests including DB / APIs
★Unit Testing
★Unit Testing
“unit testing is a software verification and validation method in which a programmer tests if individual units of source code are fit for use. A unit is the smallest testable part of an application”- wikipedia
★Unit Testing★Can improve code quality -> quick error
discovery
★Code confidence via immediate verification
★Can expose high coupling
★Will encourage refactoring to produce > testable code
★Remember: Testing is all about behavior and expectations
★Styles – TDD vs BDD
★TDD = Test Driven Development
○Write Tests
○Run them and they Fail
○Write Functions to Fulfill the Tests
○Tests should pass
○Refactor in confidence
★Test focus on Functionality
★Styles – TDD vs BDD
★BDD = Behavior Driven DevelopmentActually similar to TDD except:
★Focuses on Behavior and Specifications
★Specs (tests) are fluent and readable
★Readability makes them great for all levels of testing in the organization
★Hard to find TDD examples in JS that are not using BDD describe and it blocks
★TDD Example
Test( ‘Email address must not be blank’, function(){
notEqual(email, “”, "failed");});
★BDD ExampleDescribe( ‘Email Address’, function(){
It(‘should not be blank’, function(){
expect(email).not.toBe(“”);});
});
★Matchers
expect(true).toBe(true);expect(true).toBe(true);expect(true).toBe(true);expect(true).toBe(true);
★Matchers
expect(true).not.toBe(true);expect(true).not.toBe(true);expect(true).not.toBe(true);expect(true).not.toBe(true);expect(true).not.toBe(true);
★Matcher Samples
expect(true).toBe(true);
expect(a).not.toBe(null);
expect(a).toEqual(12);
expect(message).toMatch(/bar/);
expect(message).toMatch("bar");
expect(message).not.toMatch(/quux/);
expect(a.foo).toBeDefined();
expect(a.bar).not.toBeDefined();
★Different Testing Environments?
NodeJS - CLI In the Browser
★Overview of Testing Tools★There are a few choices
★Main Testing Players★Jasmine, Mocha and QUnit
★Jasmine★Jasmine comes ready to go out of the box
★Fluent Syntax – BDD Style
★Includes lots of matchers
★Has spies included
★Very popular, lots of support
★Angular uses Jasmine with Karma (CLI)
★Headless running and plays well with CI servers
★Jasmine - Cons
★Async testing in 1.3 can be a headache
★Expects *spec.js suffix for test files
○This can be modified depending on how you are running the tests
★Jasmine – Sample Test
describe("Hello world function", function() {
it(”contains the word world", function() {
expect(helloWorld()).toContain("world");
});
});
★Mocha★Simple Setup
★Simple Async testing
★Works great with other Assertion libraries like Chai ( not included )
★Solid Support with CI Servers, with Plugins for others
★Opinion says Mocha blazing the trail for new features
★Mocha - Cons★Requires other Libraries for key features
○No Assertion Library included
○No Mocking / Spied included
○Need to create the runner manually
★Newer to the game so not as popular or supported as others but gaining traction.
★Mocha – BDD Sample Testvar expect = require('chai').expect;
describe(’Hello World Function', function(){
it('should contain the word world', function(){
expect(helloWorld()).to.contain(’world');
})
})
★QUnit
★The oldest of the main testing frameworks
★Is popular due to use in jQuery and age
★Ember’s default Unit testing Framework
★QUnit - Cons
★Development slowed down since 2013 (but still under development)
★Syntax – No BDD style
★Assertion libraries – limited matchers
★QUnit – Sample TestQUnit.test( "ok test", function( assert ) {
assert.ok( true, "true succeeds" );
assert.ok( "non-empty", "non-empty string succeeds" );
assert.ok( false, "false fails" );
assert.ok( 0, "0 fails" );
assert.ok( NaN, "NaN fails" );
assert.ok( "", "empty string fails" );
assert.ok( null, "null fails" );
assert.ok( undefined, "undefined fails" );
});
★Spaghetti Javascript
Photo Credit – Kombination http://www.kombination.co.za/wp-content/uploads/2012/10/baby_w_spaghetti_mess_4987941.jpg
★Spaghetti Javascript ExampleIf we have time at the end
★Spaghetti Javascript Example
★Refactoring Spaghetti
★Things to refactor to make your code testable
○Code should not be one big chunk of Javascript in onReady()
○Deep nested callbacks & Anon functions cannot easily be singled out and tested
○Remove Tight Coupling – DOM access for example
★Object Literals
var personObjLit = {
ssn: ’xxxxxxxx',age: '35',name: 'Gavin Pickin',
getAge: function(){return this.age;
},getName: function() {
return this.name;}
};
★Module Pattern
var personObjLit2 = function() {ssn = ’xxxxxxx';age = '35';name = 'Gavin Pickin’;return {
getAge: function(){return age;
},getName: function() {
return name;}
};};
★Using Testing in your Workflow
★Using HTML Test Runners
○Keep a Browser open○F5 refresh tests
★Command Line Tests
★Run Jasmine – manual
○Run tests at the end of each section of work
★Run Grunt-Watch – automatic
○Runs Jasmine on every file change
○Grunt can run other tasks as well, minification etc
★Testing in your IDE
★Browser Views
○Eclipse allows you to open files in web view – uses HTML Runner
★Run Jasmine / Grunt / Karma in IDE Console
○Easy to setup – See Demo– Sublime Text 2
★Live Demo and Examples
*Install / Run Jasmine Standalone for Browser
*Install / Run Jasmine with NodeJs
*Install/ Run Jasmine with Grunt Watch
*Install / Run Grunt Watch inside Sublime Text 2
★Install / Run Jasmine for In-Browser Testing
Download standalone package from Github (I have 2.1.3)
https://github.com/jasmine/jasmine/tree/master/dist
Unzip into your /tests folder
Run /tests/SpecRunner.html to see example tests
★Standalone Jasmine
★Installing Jasmine for in Browser Testing
★SpecRunner Setup Jasmine Browser Test
<!DOCTYPE html><html><head> <meta charset="utf-8"> <title>Jasmine Spec Runner v2.1.3</title> <link rel="shortcut icon" type="image/png" href="lib/jasmine-2.1.3/jasmine_favicon.png"> <link rel="stylesheet" href="lib/jasmine-2.1.3/jasmine.css”> <script src="lib/jasmine-2.1.3/jasmine.js"></script> <script src="lib/jasmine-2.1.3/jasmine-html.js"></script> <script src="lib/jasmine-2.1.3/boot.js"></script> <!-- include source files here... --> <script src="../js/services/loginService.js"></script> <!-- include spec files here... --> <script src="spec/loginServiceSpec.js"></script></head><body></body></html>
★Installing Jasmine with NodeJS
Assuming you have NodeJs Installed… install Jasmine
$ npm install jasmine
[email protected] node_modules/jasmine
└── [email protected] ([email protected], [email protected])
★Installing Jasmine with NodeJS
Once Jasmine is installed in your project
$ Jasmine init
★Installing Jasmine with NodeJS
Edit Jasmine.json to update Locations for Spec Files and Helper Files
{ "spec_dir": "spec", "spec_files": [ "**/*[sS]pec.js" ], "helpers": [ "helpers/**/*.js" ]}
★Running Jasmine Tests with NodeJS$ Jasmine
StartedFFailures:1) A suite contains spec with an expectation Message: Expected true to be false. Stack: Error: Expected true to be false. at Object.<anonymous> (/Users/gavinpickin/Dropbox/Apps/testApp/www/spec/test_spec.js:3:18)1 spec, 1 failureFinished in 0.009 seconds
★Running Jasmine Tests with NodeJS
★Jasmine-Node is great for Node
★Jasmine Node doesn’t have a headless browser
★Hard to test Browser code
★So what should I use?
★Installing Jasmine with Grunt Watcher
★ Install Gruntnpm install grunt
★ Install Grunt – Jasminenpm install grunt-contrib-jasmine
★ Install Grunt – Watchnpm install grunt-contrib-watch
★Note: On Mac, I also needed to install Grunt CLInpm install –g grunt-cli
★Configuring Jasmine with Grunt Watcher
// gruntfile.js - https://gist.github.com/gpickin/1e1e7902d1d3676d23c5
module.exports = function (grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('node_modules/grunt/package.json'),
jasmine: {
all: {
src: ['js/*.js' ],
options: {
//'vendor': ['path/to/vendor/libs/*.js'],
'specs': ['specs/*.js' ]
}
}
},
★Configuring Jasmine with Grunt Watcher
// gruntfile.js part 2
watch: {
js: {
files: [
'js/*.js',
'specs/*.js',
],
tasks: ['jasmine:all']
}
}
});
★Configuring Jasmine with Grunt Watcher
// gruntfile.js part 3
grunt.loadNpmTasks('grunt-contrib-jasmine');
grunt.loadNpmTasks('grunt-contrib-watch');
};
★Example Spec Jasmine with Grunt Watcher
describe("A suite", function() { it("contains spec with an expectation", function() { expect(true).toBe(true);
});
});
★Running Jasmine with Grunt Watcher
★Running Jasmine with Grunt Watcher
★Running in Sublime Text 2
★Install PackageControl into Sublime Text
★Install Grunt from PackageControl
○https://packagecontrol.io/packages/Grunt
★Update Grunt Sublime Settings for paths{
"exec_args": { "path": "/bin:/usr/bin:/usr/local/bin” }
}
★Then Command Shift P – grunt
★Running in Sublime Text 2
★Refactoring Spaghetti
★Lets look at some code
★This isn’t BEST PRACTICE, its BETTER PRACTICE than you were doing
★Its not really refactoring if you don’t have tests, its
“moving code and asking for trouble”
★Q&A
★Any questions?