86
* How do I Write Testable Javascript so I can Test my CF API on Server and Client?

How do I write Testable Javascript so I can Test my CF API on Server and Client

Embed Size (px)

Citation preview

Page 1: How do I write Testable Javascript so I can Test my CF API on Server and Client

*How do I Write Testable Javascript

so I can Test my CF API on Server

and Client?

Page 2: How do I write Testable Javascript so I can Test my CF API on Server and Client

*Agenda

*Who Am I?*State of the Room?*CF API *Ways to test your API?*Overview of Testing Tools*Using Testing in your Workflow*Installing Jasmine*Installing Testbox*Live Demo

Page 3: How do I write Testable Javascript so I can Test my CF API on Server and Client

*Who Am I?

*Gavin Pickin – developing Web Apps since late 90s*New Addition to Ortus Solutions*ContentBox Evangelist

*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.

Page 4: How do I write Testable Javascript so I can Test my CF API on Server and Client

*State of the Room

*A few questions for you guys*If you have arms, use them.

Page 5: How do I write Testable Javascript so I can Test my CF API on Server and Client

*CF API

*Most CF Apps are moving towards providing an API for multiple consumers*CF has many REST API Solutions and even more with CF 12 coming soon*Built in CF*Built in Railo/Lucee*Coldbox API*Taffy

Page 6: How do I write Testable Javascript so I can Test my CF API on Server and Client

*Ways to Test your Code*Click around in the browser yourself

*Setup Selenium / Web Driver to click around for you

*Structured Programmatic Tests

Page 7: How do I write Testable Javascript so I can Test my CF API on Server and Client

*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

Page 8: How do I write Testable Javascript so I can Test my CF API on Server and Client

*Levels of Testing

Page 9: How do I write Testable Javascript so I can Test my CF API on Server and Client

*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

Page 10: How do I write Testable Javascript so I can Test my CF API on Server and Client

*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

Page 11: How do I write Testable Javascript so I can Test my CF API on Server and Client

*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

Page 12: How do I write Testable Javascript so I can Test my CF API on Server and Client

*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

Page 13: How do I write Testable Javascript so I can Test my CF API on Server and Client

*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

Page 14: How do I write Testable Javascript so I can Test my CF API on Server and Client

*TDD Example

Test( ‘Email address must not be blank’, function(){

notEqual(email, “”, "failed");});

Page 15: How do I write Testable Javascript so I can Test my CF API on Server and Client

*BDD Example

Describe( ‘Email Address’, function(){

It(‘should not be blank’, function(){expect(email).not.toBe(“”);});

});

Page 16: How do I write Testable Javascript so I can Test my CF API on Server and Client

*Matchers

expect(true).toBe(true);expect(true).toBe(true);expect(true).toBe(true);expect(true).toBe(true);

Page 17: How do I write Testable Javascript so I can Test my CF API on Server and Client

*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);

Page 18: How do I write Testable Javascript so I can Test my CF API on Server and Client

*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();

Page 19: How do I write Testable Javascript so I can Test my CF API on Server and Client

*Different Testing Environments?

NodeJS - CLI In the Browser

Page 20: How do I write Testable Javascript so I can Test my CF API on Server and Client

*CF Testing Tools

*MxUnit was the standard

*TestBox is the new standard

*Other options

Page 21: How do I write Testable Javascript so I can Test my CF API on Server and Client

*TestBoxTestBox is a next generation testing framework for ColdFusion (CFML) that is based on BDD (Behavior Driven Development) for providing a clean obvious syntax for writing tests. It contains not only a testing framework, runner, assertions and expectations library but also ships with MockBox, A Mocking & Stubbing Framework,. It also supports xUnit style of testing and MXUnit compatibilities.

Page 22: How do I write Testable Javascript so I can Test my CF API on Server and Client

*TestBox TDD Examplefunction testHelloWorld(){          $assert.includes( helloWorld(), ”world" );     }

Page 23: How do I write Testable Javascript so I can Test my CF API on Server and Client

*TestBox BDD Exampledescribe("Hello world function", function() { it(”contains the word world", function() { expect(helloWorld()).toContain("world"); });});

Page 24: How do I write Testable Javascript so I can Test my CF API on Server and Client

*TestBox New BDD Examplefeature( "Box Size", function(){

     describe( "In order to know what size box I need              As a distribution manager              I want to know the volume of the box", function(){         scenario( "Get box volume", function(){            given( "I have entered a width of 20                And a height of 30                And a depth of 40", function(){                when( "I run the calculation", function(){                      then( "the result should be 24000", function(){                          // call the method with the arguments and test the outcome                          expect( myObject.myFunction(20,30,40) ).toBe( 24000 );                      });                 });            });        });    });});

Page 25: How do I write Testable Javascript so I can Test my CF API on Server and Client

*JS Testing Tools*There are a few choices

Page 26: How do I write Testable Javascript so I can Test my CF API on Server and Client

*Main JS Testing Players*Jasmine, Mocha and QUnit

Page 27: How do I write Testable Javascript so I can Test my CF API on Server and Client

*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

Page 28: How do I write Testable Javascript so I can Test my CF API on Server and Client

*Jasmine - Cons

*Async testing in 1.3 can be a headache*Async testing in 2.0 is hard to find blog posts on (I need to write one)

*Expects *spec.js suffix for test files*This can be modified depending on how you are running the tests

Page 29: How do I write Testable Javascript so I can Test my CF API on Server and Client

*Jasmine – Sample Test

describe("Hello world function", function() { it(”contains the word world", function() { expect(helloWorld()).toContain("world"); });});

Page 30: How do I write Testable Javascript so I can Test my CF API on Server and Client

*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

Page 31: How do I write Testable Javascript so I can Test my CF API on Server and Client

*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.

Page 32: How do I write Testable Javascript so I can Test my CF API on Server and Client

*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'); })})

Page 33: How do I write Testable Javascript so I can Test my CF API on Server and Client

*QUnit

*The oldest of the main testing frameworks

*Is popular due to use in jQuery and age

*Ember’s default Unit testing Framework

Page 34: How do I write Testable Javascript so I can Test my CF API on Server and Client

*QUnit - Cons

*Development slowed down since 2013 (but still under development)*Syntax – No BDD style *Assertion libraries – limited matchers

Page 35: How do I write Testable Javascript so I can Test my CF API on Server and Client

*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" );});

Page 36: How do I write Testable Javascript so I can Test my CF API on Server and Client

*Spaghetti Javascript

Photo Credit – Kombination http://www.kombination.co.za/wp-content/uploads/2012/10/baby_w_spaghetti_mess_4987941.jpg

Page 37: How do I write Testable Javascript so I can Test my CF API on Server and Client

*Spaghetti Javascript

Example

Page 38: How do I write Testable Javascript so I can Test my CF API on Server and Client

*Spaghetti Javascript

Example

Page 39: How do I write Testable Javascript so I can Test my CF API on Server and Client

*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

Page 40: How do I write Testable Javascript so I can Test my CF API on Server and Client

*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”Kev McCabe

Page 41: How do I write Testable Javascript so I can Test my CF API on Server and Client

*Object Literals

var personObjLit = {

ssn: ’xxxxxxxx',age: '35',name: 'Gavin Pickin',

getAge: function(){return this.age;},getName: function() {return this.name;}};

Page 42: How do I write Testable Javascript so I can Test my CF API on Server and Client

*Module Pattern

var personObjLit2 = function() {ssn = ’xxxxxxx';age = '35';name = 'Gavin Pickin’;return {getAge: function(){return age;},getName: function() {return name;}};};

Page 43: How do I write Testable Javascript so I can Test my CF API on Server and Client

*Using Testing in your Workflow

*Using HTML Test Runners*Keep a Browser open*F5 refresh tests

Page 44: How do I write Testable Javascript so I can Test my CF API on Server and Client

*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

Page 45: How do I write Testable Javascript so I can Test my CF API on Server and Client

*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*Fairly Easy to setup*See Demo– Sublime Text 2 (if we have time)

Page 46: How do I write Testable Javascript so I can Test my CF API on Server and Client

*Live Demo and Examples

*Install / Run Jasmine Standalone for Browser*Install / Run Jasmine with NodeJs*Install / Run Jasmine with Grunt Watch*Install / Run Testbox in Browser*Install / Run Testbox with Grunt Watch*Install / Run Grunt Watch inside Sublime Text 2

Page 47: How do I write Testable Javascript so I can Test my CF API on Server and Client

*Install / Run Jasmine for In-

Browser TestingDownload 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

Page 48: How do I write Testable Javascript so I can Test my CF API on Server and Client

*Standalone Jasmine

Page 49: How do I write Testable Javascript so I can Test my CF API on Server and Client

*Installing Jasmine for in Browser

Testing

http://www.testableapi.local.com:8504/tests/SpecRunner.html

Page 50: How do I write Testable Javascript so I can Test my CF API on Server and Client

*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>

Page 51: How do I write Testable Javascript so I can Test my CF API on Server and Client

*Installing Jasmine with NodeJS

Assuming you have NodeJs Installed… install Jasmine

$ npm install [email protected] node_modules/jasmine├── [email protected]├── [email protected]└── [email protected] ([email protected], [email protected])

Page 52: How do I write Testable Javascript so I can Test my CF API on Server and Client

*Installing Jasmine with NodeJS

Once Jasmine is installed in your project

$ Jasmine init

Page 53: How do I write Testable Javascript so I can Test my CF API on Server and Client

*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" ]}

Page 54: How do I write Testable Javascript so I can Test my CF API on Server and Client

*Running Jasmine Tests with NodeJS

$ JasmineStartedFFailures: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

Page 55: How do I write Testable Javascript so I can Test my CF API on Server and Client

*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?

Page 56: How do I write Testable Javascript so I can Test my CF API on Server and Client

*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

Page 57: How do I write Testable Javascript so I can Test my CF API on Server and Client

*Configuring Jasmine with

Grunt Watcher// gruntfile.js - https://gist.github.com/gpickin/1e1e7902d1d3676d23c5module.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' ], '--web-security': false

}}

},

Page 58: How do I write Testable Javascript so I can Test my CF API on Server and Client

*Configuring Jasmine with

Grunt Watcher// gruntfile.js part 2watch: {

js: {files: [

'js/*.js','specs/*.js',

],tasks: ['jasmine:all']

}}

});

Page 59: How do I write Testable Javascript so I can Test my CF API on Server and Client

*Configuring Jasmine with

Grunt Watcher// gruntfile.js part 3

grunt.loadNpmTasks('grunt-contrib-jasmine');grunt.loadNpmTasks('grunt-contrib-watch');

};

Page 60: How do I write Testable Javascript so I can Test my CF API on Server and Client

*Example Jasmine Spec with Grunt

Watcherdescribe("Forgotten Password Form", function() { it("should warn you if the email is invalid before making Ajax Call", function() { expect( isEmailInputInvalid('') ).toBe(true); expect( isEmailInputInvalid('dddddddddd') ).toBe(true); expect( isEmailInputInvalid('dddddd@') ).toBe(true); expect( isEmailInputInvalid('dddddd@ddddd') ).toBe(true); expect( isEmailInputInvalid('dddddd@ddddddd.') ).toBe(true); expect( isEmailInputInvalid('[email protected]') ).toBe(false); });});

Page 61: How do I write Testable Javascript so I can Test my CF API on Server and Client

*Example Jasmine Spec with Grunt

Watcherdescribe("Login Form", function() { it("should set status correct status message with successful Ajax Response", function() { spyOn( window, "setStatusMessage"); processLoginAjaxDone('{"RESULT":"200"}'); expect(setStatusMessage).toHaveBeenCalled(); expect(setStatusMessage).toHaveBeenCalledWith( ‘TARDIS Access Granted - Please wait for the Doctor to take you for a spin'); });});

Page 62: How do I write Testable Javascript so I can Test my CF API on Server and Client

*Example Jasmine Spec with Grunt

Watcherdescribe("Login API", function() { it("should return a failing Ajax Response", function() { spyOn( window, "processLoginAjaxDone"); loginButtonEventHandlerProcess( '[email protected]', 'password'); expect(processLoginAjaxDone).toHaveBeenCalled(); expect(processLoginAjaxDone).toHaveBeenCalledWith( ‘{"RESULT":400}'); expect(processLoginAjaxFail).not.toHaveBeenCalled(); });});

Page 63: How do I write Testable Javascript so I can Test my CF API on Server and Client

*Whats wrong with that?

describe("Login API", function() { it("should return a failing Ajax Response", function() { spyOn( window, "processLoginAjaxDone"); loginButtonEventHandlerProcess( '[email protected]', 'password'); expect(processLoginAjaxDone).toHaveBeenCalled(); expect(processLoginAjaxDone).toHaveBeenCalledWith( ‘{"RESULT":400}'); expect(processLoginAjaxFail).not.toHaveBeenCalled(); });});

Page 64: How do I write Testable Javascript so I can Test my CF API on Server and Client

*Unit Tests and Async Calls

*You want Unit Tests to test the unit and not it’s dependencies*You want Unit Tests to run quick*You should mock the API in the Ajax call

*But we want to test the API*So essentially, we’re writing an integration test.

Page 65: How do I write Testable Javascript so I can Test my CF API on Server and Client

*How to wait for Async

describe("Login API", function() {beforeEach(function( done ) { spyOn( window, "processLoginAjaxDone").and.callFake( function(){ done(); }); spyOn( window, "processLoginAjaxFail").and.callFake( function(){ done(); }); loginButtonEventHandlerProcess('[email protected]', 'password');});it("should return a failing Ajax Response", function() { });

});

Page 66: How do I write Testable Javascript so I can Test my CF API on Server and Client

*How to wait for Async

describe("Login API", function() {beforeEach(function( done ) { … });it("should return a failing Ajax Response", function() { expect(processLoginAjaxDone).toHaveBeenCalled(); expect(processLoginAjaxDone).toHaveBeenCalledWith( '{"RESULT":400}'); expect(processLoginAjaxFail).not.toHaveBeenCalled();});

});

Page 67: How do I write Testable Javascript so I can Test my CF API on Server and Client

*Running Jasmine with Grunt

Watcher

Page 68: How do I write Testable Javascript so I can Test my CF API on Server and Client

*Running Jasmine with Grunt

Watcher

Page 69: How do I write Testable Javascript so I can Test my CF API on Server and Client

*Installing Testbox

*Install Testbox – Thanks to Commandbox*box install testbox

*Decide how you want to run Testbox

Page 70: How do I write Testable Javascript so I can Test my CF API on Server and Client

*Create a runner.cfm

*<cfsetting showDebugOutput="false">*<!--- Executes all tests in the 'specs' folder with simple reporter by

default --->*<cfparam name="url.reporter" default="simple">*<cfparam name="url.directory" default="tests.specs">*<cfparam name="url.recurse" default="true"

type="boolean">*<cfparam name="url.bundles" default="">*<cfparam name="url.labels" default="">

*<!--- Include the TestBox HTML Runner --->*<cfinclude template="/testbox/system/runners/HTMLRunner.cfm"

>

Page 71: How do I write Testable Javascript so I can Test my CF API on Server and Client

*Create a Test Suite// tests/specs/CFCTest.cfccomponent extends="testbox.system.BaseSpec" { function run() { it( "will error with incorrect login", function(){ var oTest = new cfcs.userServiceRemote(); expect( oTest.login( '[email protected]', 'topsecret').result ).toBe('400'); }); }}

Page 72: How do I write Testable Javascript so I can Test my CF API on Server and Client

*Create a 2nd Test Suite// tests/specs/APITest.cfccomponent extends="testbox.system.BaseSpec" {

function run() { describe("userService API Login", function(){ it( "will error with incorrect login", function(){ var email = "[email protected]"; var password = "topsecret”; var result = ""; http url="http://www.testableapi.local.com:8504/cfcs/userServiceRemote.cfc?method=login&email=#email#&password=#password#" result="result”; expect( DeserializeJSON(result.filecontent).result ).toBe('400'); }); }); }}

Page 73: How do I write Testable Javascript so I can Test my CF API on Server and Client

*Running Testbox with runner.cfm

*Install Testbox Runner – Thanks Sean Coyne*npm install testbox-runner

*Install Grunt Shell*npm install grunt-shell

*Add Grunt Configuration

Page 74: How do I write Testable Javascript so I can Test my CF API on Server and Client

*Running Testbox with Grunt Watch

*Install Testbox Runner – Thanks Sean Coyne*npm install testbox-runner

*Install Grunt Shell*npm install grunt-shell

*Add Grunt Configuration

Page 75: How do I write Testable Javascript so I can Test my CF API on Server and Client

*Adding TextBox Config 1

module.exports = function (grunt) {

grunt.loadNpmTasks('grunt-shell');

grunt.initConfig({ … })

}

Page 76: How do I write Testable Javascript so I can Test my CF API on Server and Client

*Adding TextBox Config 2

Watch: { … cfml: { files: [ "cfcs/*.cfc"], tasks: [ "testbox" ] }}

Page 77: How do I write Testable Javascript so I can Test my CF API on Server and Client

*Adding TextBox Config 3

shell: { testbox: { command: "./node_modules/testbox-runner/index.js --colors --runner http://www.testableapi.local.com:8504/tests/runner.cfm --directory /tests/specs --recurse true” }}

Page 78: How do I write Testable Javascript so I can Test my CF API on Server and Client

*Adding TextBox Config 4

grunt.registerTask("testbox", [ "shell:testbox" ]);grunt.loadNpmTasks('grunt-contrib-jasmine');grunt.loadNpmTasks('grunt-contrib-watch');

Page 79: How do I write Testable Javascript so I can Test my CF API on Server and Client

*Adding TextBox Config 5

js: { files: [ 'js/*.js', 'specs/*.js', "cfcs/*.cfc” ], tasks: ['jasmine:all']},

Page 80: How do I write Testable Javascript so I can Test my CF API on Server and Client

*GruntFile.js Gists

Jasminehttps://gist.github.com/gpickin/1e1e7902d1d3676d23c5

Jasmine + Testboxhttps://gist.github.com/gpickin/9fc82df3667eeb63c7e7

Page 81: How do I write Testable Javascript so I can Test my CF API on Server and Client

*Testbox output with Grunt

Page 83: How do I write Testable Javascript so I can Test my CF API on Server and Client

*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

Page 84: How do I write Testable Javascript so I can Test my CF API on Server and Client

*Running in Sublime Text 2

Page 86: How do I write Testable Javascript so I can Test my CF API on Server and Client

*More Sessions

https://www.ortussolutions.com/odw

FREE ONLINE