how do i write testable javascript?
TRANSCRIPT
*
* * 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
* * Gavin Pickin – developing Web Apps since late 90s
* What else do you need to know? * Blog - http://www.gpickin.com
* Twitter – http://twitter.com/gpickin
* Github - https://github.com/gpickin
* Lets get on with the show.
* * A few questions for you guys
* If you have arms, use them.
* * Click around in the browser yourself
* Setup Selenium / Web Driver to click around for you
* Structured Programmatic Tests
* * 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
* * 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 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
* * 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
* * 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
* * BDD = Behavior Driven Development Actually 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
* Test( ‘Email address must not be blank’, function(){
notEqual(email, “”, "failed");
});
* Describe( ‘Email Address’, function(){
It(‘should not be blank’, function(){
expect(email).not.toBe(“”);
});
});
*
expect(true).toBe(true);
expect(true).toBe(true);
expect(true).toBe(true); expect(true).toBe(true);
*
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);
* 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();
*
NodeJS - CLI In the Browser
* * There are a few choices
* * Jasmine, Mocha and QUnit
* * 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
*
* 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
*
describe("Hello world function", function() {
it(”contains the word world", function() {
expect(helloWorld()).toContain("world");
});
});
* * 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
* * 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.
* var expect = require('chai').expect;
describe(’Hello World Function', function(){
it('should contain the word world', function(){
expect(helloWorld()).to.contain(’world');
})
})
*
* The oldest of the main testing frameworks
* Is popular due to use in jQuery and age
* Ember’s default Unit testing Framework
*
* Development slowed down since 2013 (but still under development)
* Syntax – No BDD style
* Assertion libraries – limited matchers
* QUnit.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" );
});
*
Photo Credit – Kombination http://www.kombination.co.za/wp-content/uploads/2012/10/baby_w_spaghetti_mess_4987941.jpg
*
*
*
* 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
*
* 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”
* var personObjLit = {
ssn: ’xxxxxxxx', age: '35', name: 'Gavin Pickin', getAge: function(){ return this.age; }, getName: function() { return this.name; } };
* var personObjLit2 = function() {
ssn = ’xxxxxxx'; age = '35'; name = 'Gavin Pickin’; return { getAge: function(){ return age; }, getName: function() { return name; } }; };
* * Using HTML Test Runners * Keep a Browser open
* F5 refresh 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
* * 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
*
* 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
*
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
*
*
* <!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>
*
Assuming you have NodeJs Installed… install Jasmine
$ npm install jasmine
[email protected] node_modules/jasmine
└── [email protected] ([email protected], [email protected])
*
Once Jasmine is installed in your project
$ Jasmine init
* Edit Jasmine.json to update Locations for Spec Files and Helper Files { "spec_dir": "spec", "spec_files": [ "**/*[sS]pec.js" ],
"helpers": [ "helpers/**/*.js" ] }
* $ Jasmine Started F Failures: 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 failure Finished in 0.009 seconds
*
* Jasmine-Node is great for Node
* Jasmine Node doesn’t have a headless browser
* Hard to test Browser code
* So what should I use?
*
* Install Grunt npm install grunt
* Install Grunt – Jasmine npm install grunt-contrib-jasmine
* Install Grunt – Watch npm install grunt-contrib-watch
* Note: On Mac, I also needed to install Grunt CLI npm install –g grunt-cli
* // 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' ]
}
}
},
*
// gruntfile.js part 2
watch: {
js: {
files: [
'js/*.js',
'specs/*.js',
],
tasks: ['jasmine:all']
}
}
});
*
// gruntfile.js part 3
grunt.loadNpmTasks('grunt-contrib-jasmine');
grunt.loadNpmTasks('grunt-contrib-watch');
};
*
describe("A suite", function() {
it("contains spec with an expectation", function() {
expect(true).toBe(true);
});
});
*
*
* * 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
*
* * Any questions?
* Come check out my Cordova Hooks session and see how you can run Unit Tests (and much more) whenever you’re preparing a build for your cordova app.