*
* *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
* *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.
* *A few questions for you guys *If you have arms, use them.
* *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
* *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
* *MxUnit was the standard *TestBox is the new standard *Other options
* TestBox 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.
* function testHelloWorld(){ $assert.includes( helloWorld(), ”world" ); }
* describe("Hello world function", function() { it(”contains the word world", function() { expect(helloWorld()).toContain("world"); }); });
* feature( "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 ); }); });
* *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 *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
* 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” Kev McCabe
* 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 *Fairly Easy to setup *See Demo– Sublime Text 2 (if we have time)
* *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
* 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
*
* http://www.testableapi.local.com:8504/tests/SpecRunner.html
* <!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 jasmine@2.2.1 node_modules/jasmine ├── exit@0.1.2 ├── jasmine-core@2.2.0 └── glob@3.2.11 (inherits@2.0.1, minimatch@0.3.0)
* 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' ], '--web-security': false } }
* // 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("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('dddddd@ddddddd.com') ).toBe(false); }); });
* describe("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'); }); });
* describe("Login API", function() { it("should return a failing Ajax Response", function() { spyOn( window, "processLoginAjaxDone"); loginButtonEventHandlerProcess( 'gavin@gavin.co.nz', 'password'); expect(processLoginAjaxDone).toHaveBeenCalled(); expect(processLoginAjaxDone).toHaveBeenCalledWith( ‘{"RESULT":400}'); expect(processLoginAjaxFail).not.toHaveBeenCalled(); }); });
* describe("Login API", function() { it("should return a failing Ajax Response", function() { spyOn( window, "processLoginAjaxDone"); loginButtonEventHandlerProcess( 'gavin@gavin.co.nz', 'password'); expect(processLoginAjaxDone).toHaveBeenCalled(); expect(processLoginAjaxDone).toHaveBeenCalledWith( ‘{"RESULT":400}'); expect(processLoginAjaxFail).not.toHaveBeenCalled(); }); });
* *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.
* describe("Login API", function() { beforeEach(function( done ) { spyOn( window, "processLoginAjaxDone").and.callFake( function(){ done(); }); spyOn( window, "processLoginAjaxFail").and.callFake( function(){ done(); }); loginButtonEventHandlerProcess('gavin@gavin.co.nz', 'password'); }); it("should return a failing Ajax Response", function() { }); });
* 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(); }); });
*
*
* *Install Testbox – Thanks to Commandbox *box install testbox *Decide how you want to run Testbox
* *<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" >
* // tests/specs/CFCTest.cfc component extends="testbox.system.BaseSpec" { function run() { it( "will error with incorrect login", function(){ var oTest = new cfcs.userServiceRemote(); expect( oTest.login( 'gavin@gavin.com', 'topsecret').result ).toBe('400'); }); } }
* // tests/specs/APITest.cfc component extends="testbox.system.BaseSpec" { function run() { describe("userService API Login", function(){ it( "will error with incorrect login", function(){ var email = "gavin@gavin.com"; var password = "topsecret”; var result = ""; http url="http://www.testableapi.local.com:8504/cfcs/userServiceRemote.cfc?metho d=login&email=#email#&password=#password#" result="result”; expect( DeserializeJSON(result.filecontent).result ).toBe('400'); }); }); } }
* *Install Testbox Runner – Thanks Sean Coyne *npm install testbox-runner *Install Grunt Shell *npm install grunt-shell *Add Grunt Configuration
* *Install Testbox Runner – Thanks Sean Coyne *npm install testbox-runner *Install Grunt Shell *npm install grunt-shell *Add Grunt Configuration
* module.exports = function (grunt) { grunt.loadNpmTasks('grunt-shell'); grunt.initConfig({ … }) }
* Watch: { … cfml: { files: [ "cfcs/*.cfc"], tasks: [ "testbox" ] } }
* shell: { testbox: { command: "./node_modules/testbox- runner/index.js --colors --runner http://www.testableapi.local.com:8504/tests/r unner.cfm --directory /tests/specs --recurse true” } }
* grunt.registerTask("testbox", [ "shell:testbox" ]); grunt.loadNpmTasks('grunt-contrib-jasmine'); grunt.loadNpmTasks('grunt-contrib-watch');
* js: { files: [ 'js/*.js', 'specs/*.js', "cfcs/*.cfc” ], tasks: ['jasmine:all'] },
* Jasmine https://gist.github.com/gpickin/1e1e7902d1d3 676d23c5 Jasmine + Testbox https://gist.github.com/gpickin/9fc82df3667ee b63c7e7
*
* *Testbox has several runners, you have seen the HTML one, this Runner uses the JSON runner and then formats it. *http://www.testableapi.local.com:8504/tests/run ner.cfm?reporter=JSON&directory=%2Ftests%2Fspec s&recurse=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? *Interesting Link: https://medium.com/@Zyklus/beautiful-seamless- javascript-testing-in-10-minutes-2a743637035b
* https://www.ortussolutions.com/odw FREE ONLINE

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

  • 1.
  • 2.
    * *Who Am I? *Stateof the Room? *CF API *Ways to test your API? *Overview of Testing Tools *Using Testing in your Workflow *Installing Jasmine *Installing Testbox *Live Demo
  • 3.
    * *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.
  • 4.
    * *A few questionsfor you guys *If you have arms, use them.
  • 5.
    * *Most CF Appsare 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
  • 6.
    * *Click around inthe browser yourself *Setup Selenium / Web Driver to click around for you *Structured Programmatic Tests
  • 7.
    * *Black/White Box *Unit Testing *IntegrationTesting *Functional Tests *System Tests *End to End Tests *Sanity Testing *Regression Test *Acceptance Tests *Load Testing *Stress Test *Performance Tests *Usability Tests *+ More
  • 8.
  • 9.
    * *Integration Tests severalof 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
  • 10.
    * “unit testing isa 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
  • 11.
    * *Can improve codequality -> 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
  • 12.
    * *TDD = TestDriven Development *Write Tests *Run them and they Fail *Write Functions to Fulfill the Tests *Tests should pass *Refactor in confidence *Test focus on Functionality
  • 13.
    * *BDD = BehaviorDriven 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
  • 14.
    * Test( ‘Email addressmust not be blank’, function(){ notEqual(email, “”, "failed"); });
  • 15.
    * Describe( ‘Email Address’, function(){ It(‘shouldnot be blank’, function(){ expect(email).not.toBe(“”); }); });
  • 16.
  • 17.
  • 18.
  • 19.
    * NodeJS - CLIIn the Browser
  • 20.
    * *MxUnit was thestandard *TestBox is the new standard *Other options
  • 21.
    * TestBox is anext 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.
  • 22.
  • 23.
    * describe("Hello world function",function() { it(”contains the word world", function() { expect(helloWorld()).toContain("world"); }); });
  • 24.
    * feature( "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 ); }); });
  • 25.
    * *There are afew choices
  • 26.
  • 27.
    * *Jasmine comes readyto 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
  • 28.
    * *Async testing in1.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
  • 29.
    * describe("Hello world function",function() { it(”contains the word world", function() { expect(helloWorld()).toContain("world"); }); });
  • 30.
    * *Simple Setup *Simple Asynctesting *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
  • 31.
    * *Requires other Librariesfor 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.
  • 32.
    * var expect =require('chai').expect; describe(’Hello World Function', function(){ it('should contain the word world', function(){ expect(helloWorld()).to.contain(’world'); }) })
  • 33.
    * *The oldest ofthe main testing frameworks *Is popular due to use in jQuery and age *Ember’s default Unit testing Framework
  • 34.
    * *Development slowed downsince 2013 (but still under development) *Syntax – No BDD style *Assertion libraries – limited matchers
  • 35.
    * 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" ); });
  • 36.
    * Photo Credit –Kombination http://www.kombination.co.za/wp-content/uploads/2012/10/baby_w_spaghetti_mess_4987941.jpg
  • 37.
  • 38.
  • 39.
    * *Things to refactorto 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
  • 40.
    * *Lets look atsome 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
  • 41.
    * var personObjLit ={ ssn: ’xxxxxxxx', age: '35', name: 'Gavin Pickin', getAge: function(){ return this.age; }, getName: function() { return this.name; } };
  • 42.
    * var personObjLit2 =function() { ssn = ’xxxxxxx'; age = '35'; name = 'Gavin Pickin’; return { getAge: function(){ return age; }, getName: function() { return name; } }; };
  • 43.
    * *Using HTML TestRunners *Keep a Browser open *F5 refresh tests
  • 44.
    * *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
  • 45.
    * *Browser Views *Eclipse allowsyou 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)
  • 46.
    * *Install / RunJasmine 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
  • 47.
    * Download standalone packagefrom 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
  • 48.
  • 49.
  • 50.
    * <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>JasmineSpec 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>
  • 51.
    * Assuming you haveNodeJs Installed… install Jasmine $ npm install jasmine jasmine@2.2.1 node_modules/jasmine ├── exit@0.1.2 ├── jasmine-core@2.2.0 └── glob@3.2.11 (inherits@2.0.1, minimatch@0.3.0)
  • 52.
    * Once Jasmine isinstalled in your project $ Jasmine init
  • 53.
    * Edit Jasmine.json toupdate Locations for Spec Files and Helper Files { "spec_dir": "spec", "spec_files": [ "**/*[sS]pec.js" ], "helpers": [ "helpers/**/*.js" ] }
  • 54.
    * $ Jasmine Started F Failures: 1) Asuite 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
  • 55.
    * *Jasmine-Node is greatfor Node *Jasmine Node doesn’t have a headless browser *Hard to test Browser code *So what should I use?
  • 56.
    * *Install Grunt npm installgrunt *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
  • 57.
    * // 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' ], '--web-security': false } }
  • 58.
    * // gruntfile.js part2 watch: { js: { files: [ 'js/*.js', 'specs/*.js', ], tasks: ['jasmine:all'] } } });
  • 59.
    * // gruntfile.js part3 grunt.loadNpmTasks('grunt-contrib-jasmine'); grunt.loadNpmTasks('grunt-contrib-watch'); };
  • 60.
    * describe("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('dddddd@ddddddd.com') ).toBe(false); }); });
  • 61.
    * describe("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'); }); });
  • 62.
    * describe("Login API", function(){ it("should return a failing Ajax Response", function() { spyOn( window, "processLoginAjaxDone"); loginButtonEventHandlerProcess( 'gavin@gavin.co.nz', 'password'); expect(processLoginAjaxDone).toHaveBeenCalled(); expect(processLoginAjaxDone).toHaveBeenCalledWith( ‘{"RESULT":400}'); expect(processLoginAjaxFail).not.toHaveBeenCalled(); }); });
  • 63.
    * describe("Login API", function(){ it("should return a failing Ajax Response", function() { spyOn( window, "processLoginAjaxDone"); loginButtonEventHandlerProcess( 'gavin@gavin.co.nz', 'password'); expect(processLoginAjaxDone).toHaveBeenCalled(); expect(processLoginAjaxDone).toHaveBeenCalledWith( ‘{"RESULT":400}'); expect(processLoginAjaxFail).not.toHaveBeenCalled(); }); });
  • 64.
    * *You want UnitTests 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.
  • 65.
    * describe("Login API", function(){ beforeEach(function( done ) { spyOn( window, "processLoginAjaxDone").and.callFake( function(){ done(); }); spyOn( window, "processLoginAjaxFail").and.callFake( function(){ done(); }); loginButtonEventHandlerProcess('gavin@gavin.co.nz', 'password'); }); it("should return a failing Ajax Response", function() { }); });
  • 66.
    * 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(); }); });
  • 67.
  • 68.
  • 69.
    * *Install Testbox –Thanks to Commandbox *box install testbox *Decide how you want to run Testbox
  • 70.
    * *<cfsetting showDebugOutput="false"> *<!--- Executesall 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" >
  • 71.
    * // tests/specs/CFCTest.cfc component extends="testbox.system.BaseSpec"{ function run() { it( "will error with incorrect login", function(){ var oTest = new cfcs.userServiceRemote(); expect( oTest.login( 'gavin@gavin.com', 'topsecret').result ).toBe('400'); }); } }
  • 72.
    * // tests/specs/APITest.cfc component extends="testbox.system.BaseSpec"{ function run() { describe("userService API Login", function(){ it( "will error with incorrect login", function(){ var email = "gavin@gavin.com"; var password = "topsecret”; var result = ""; http url="http://www.testableapi.local.com:8504/cfcs/userServiceRemote.cfc?metho d=login&email=#email#&password=#password#" result="result”; expect( DeserializeJSON(result.filecontent).result ).toBe('400'); }); }); } }
  • 73.
    * *Install Testbox Runner– Thanks Sean Coyne *npm install testbox-runner *Install Grunt Shell *npm install grunt-shell *Add Grunt Configuration
  • 74.
    * *Install Testbox Runner– Thanks Sean Coyne *npm install testbox-runner *Install Grunt Shell *npm install grunt-shell *Add Grunt Configuration
  • 75.
    * module.exports = function(grunt) { grunt.loadNpmTasks('grunt-shell'); grunt.initConfig({ … }) }
  • 76.
    * Watch: { … cfml: { files:[ "cfcs/*.cfc"], tasks: [ "testbox" ] } }
  • 77.
    * shell: { testbox: { command:"./node_modules/testbox- runner/index.js --colors --runner http://www.testableapi.local.com:8504/tests/r unner.cfm --directory /tests/specs --recurse true” } }
  • 78.
    * grunt.registerTask("testbox", [ "shell:testbox"]); grunt.loadNpmTasks('grunt-contrib-jasmine'); grunt.loadNpmTasks('grunt-contrib-watch');
  • 79.
  • 80.
  • 81.
  • 82.
    * *Testbox has severalrunners, you have seen the HTML one, this Runner uses the JSON runner and then formats it. *http://www.testableapi.local.com:8504/tests/run ner.cfm?reporter=JSON&directory=%2Ftests%2Fspec s&recurse=true
  • 83.
    * *Install PackageControl intoSublime 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
  • 84.
  • 85.
  • 86.