86
3 WAYS TO TEST YOUR COLDFUSION API Gavin Pickin cf.Objective() 2017

3 WAYS TO TEST YOUR COLDFUSION API

Embed Size (px)

Citation preview

Page 1: 3 WAYS TO TEST YOUR COLDFUSION API

3 WAYS TO TEST YOUR COLDFUSION API

Gavin Pickincf.Objective() 2017

Page 2: 3 WAYS TO TEST YOUR COLDFUSION API

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: 3 WAYS TO TEST YOUR COLDFUSION API

Who am I?

Gavin Pickin – developing Web Apps since late 90s

● Software Consultant for 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

Let’s get on with the show.

Page 4: 3 WAYS TO TEST YOUR COLDFUSION API

State of the Room● A few questions for you guys

● If you have arms, use them.

Page 5: 3 WAYS TO TEST YOUR COLDFUSION API

APIs in CFMLMost CF Apps are moving towards providing an API for multiple consumers

CF has many REST API Solutions and even more with CF 2016

● Built in CF● Built in Railo/Lucee● Coldbox API● Taffy

Page 6: 3 WAYS TO TEST YOUR COLDFUSION API

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: 3 WAYS TO TEST YOUR COLDFUSION API

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: 3 WAYS TO TEST YOUR COLDFUSION API
Page 9: 3 WAYS TO TEST YOUR COLDFUSION API

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: 3 WAYS TO TEST YOUR COLDFUSION API

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: 3 WAYS TO TEST YOUR COLDFUSION API

Unit Testing can...

● 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: 3 WAYS TO TEST YOUR COLDFUSION API

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: 3 WAYS TO TEST YOUR COLDFUSION API

Styles - TDD vs BDD

● 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

Page 14: 3 WAYS TO TEST YOUR COLDFUSION API

TDD Example

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

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

});

Page 15: 3 WAYS TO TEST YOUR COLDFUSION API

BDD Example

Describe( ‘Email Address’, function(){

It(‘should not be blank’, function(){

expect(email).not.toBe(“”);

});

});

Page 16: 3 WAYS TO TEST YOUR COLDFUSION API

Matchers

expect(true).toBe(true);

expect(true).toBe(true);

expect(true).toBe(true);

expect(true).toBe(true);

Page 17: 3 WAYS TO TEST YOUR COLDFUSION API

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: 3 WAYS TO TEST YOUR COLDFUSION API

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: 3 WAYS TO TEST YOUR COLDFUSION API

Different Testing Environments?

NodeJS - CLI In the Browser

Page 20: 3 WAYS TO TEST YOUR COLDFUSION API

CF Testing Tools* MxUnit was the standard

* TestBox is the new standard

Other options

Page 21: 3 WAYS TO TEST YOUR COLDFUSION API

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: 3 WAYS TO TEST YOUR COLDFUSION API

TestBox TDD Examplefunction testHelloWorld(){

          $assert.includes( helloWorld(), ”world" );

     }

Page 23: 3 WAYS TO TEST YOUR COLDFUSION API

TestBox BDD Exampledescribe("Hello world function", function() {

it(”contains the word world", function() {

expect(helloWorld()).toContain("world");

});

});

Page 24: 3 WAYS TO TEST YOUR COLDFUSION API

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: 3 WAYS TO TEST YOUR COLDFUSION API

JS Testing Tools*There are a few choices

Page 26: 3 WAYS TO TEST YOUR COLDFUSION API

Main JS Testing PlayersJasmine, Mocha and QUnit

Page 27: 3 WAYS TO TEST YOUR COLDFUSION API

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: 3 WAYS TO TEST YOUR COLDFUSION API

Jasmine - ConsAsync 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: 3 WAYS TO TEST YOUR COLDFUSION API

Jasmine – Sample Test

describe("Hello world function", function() {

it(”contains the word world", function() {

expect(helloWorld()).toContain("world");

});

});

Page 30: 3 WAYS TO TEST YOUR COLDFUSION API

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: 3 WAYS TO TEST YOUR COLDFUSION API

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: 3 WAYS TO TEST YOUR COLDFUSION API

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: 3 WAYS TO TEST YOUR COLDFUSION API

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: 3 WAYS TO TEST YOUR COLDFUSION API

QUnit - Cons

*Development slowed down since 2013 (but still under development)

*Syntax – No BDD style

*Assertion libraries – limited matchers

Page 35: 3 WAYS TO TEST YOUR COLDFUSION API

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: 3 WAYS TO TEST YOUR COLDFUSION API

Spaghetti Javascript

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

Page 37: 3 WAYS TO TEST YOUR COLDFUSION API

Spaghetti Javascript Example

Page 38: 3 WAYS TO TEST YOUR COLDFUSION API

Spaghetti Javascript Example

Page 39: 3 WAYS TO TEST YOUR COLDFUSION API

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: 3 WAYS TO TEST YOUR COLDFUSION API

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: 3 WAYS TO TEST YOUR COLDFUSION API

Object Literalsvar personObjLit = {

ssn: ’xxxxxxxx',age: '35',name: 'Gavin Pickin',getAge: function(){

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

return this.name;}

};

Page 42: 3 WAYS TO TEST YOUR COLDFUSION API

Module Patternvar personObjLit2 = function() {

ssn = ’xxxxxxx';age = '35';name = 'Gavin Pickin’;return {

getAge: function(){return age;

},getName: function() {

return name;}

};};

Page 43: 3 WAYS TO TEST YOUR COLDFUSION API

Using Testing in your Workflow

*Using HTML Test Runners

*Keep a Browser open

*F5 refresh tests

Page 44: 3 WAYS TO TEST YOUR COLDFUSION API

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: 3 WAYS TO TEST YOUR COLDFUSION API

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: 3 WAYS TO TEST YOUR COLDFUSION API

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: 3 WAYS TO TEST YOUR COLDFUSION API

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

Page 48: 3 WAYS TO TEST YOUR COLDFUSION API

Standalone Jasmine

Page 49: 3 WAYS TO TEST YOUR COLDFUSION API

Installing Jasmine for in Browser Testing

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

Page 50: 3 WAYS TO TEST YOUR COLDFUSION API

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: 3 WAYS TO TEST YOUR COLDFUSION API

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] ([email protected], [email protected])

Page 52: 3 WAYS TO TEST YOUR COLDFUSION API

Installing Jasmine with NodeJS

Once Jasmine is installed in your project

$ Jasmine init

Page 53: 3 WAYS TO TEST YOUR COLDFUSION API

Installing Jasmine with NodeJSEdit Jasmine.json to update Locations for Spec Files and Helper Files{ "spec_dir": "spec", "spec_files": [ "**/*[sS]pec.js" ], "helpers": [ "helpers/**/*.js" ]}

Page 54: 3 WAYS TO TEST YOUR COLDFUSION API

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: 3 WAYS TO TEST YOUR COLDFUSION API

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: 3 WAYS TO TEST YOUR COLDFUSION API

Installing Jasmine with Grunt Watcher

* Install Grunt

npm 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: 3 WAYS TO TEST YOUR COLDFUSION API

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' ], '--web-security': false

}

}

},

Page 58: 3 WAYS TO TEST YOUR COLDFUSION API

Configuring Jasmine with Grunt Watcher// gruntfile.js part 2

watch: {

js: {

files: [

'js/*.js',

'specs/*.js',

],

tasks: ['jasmine:all']

}

}

});

Page 59: 3 WAYS TO TEST YOUR COLDFUSION API

Configuring Jasmine with Grunt Watcher

// gruntfile.js part 3

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

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

};

Page 60: 3 WAYS TO TEST YOUR COLDFUSION API

Example Jasmine Spec with Grunt Watcher

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('[email protected]') ).toBe(false);

});

});

Page 61: 3 WAYS TO TEST YOUR COLDFUSION API

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: 3 WAYS TO TEST YOUR COLDFUSION API

Example Jasmine Spec with Grunt Watcher

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 63: 3 WAYS TO TEST YOUR COLDFUSION API

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: 3 WAYS TO TEST YOUR COLDFUSION API

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: 3 WAYS TO TEST YOUR COLDFUSION API

How to wait for Asyncdescribe("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: 3 WAYS TO TEST YOUR COLDFUSION API

How to wait for Asyncdescribe("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: 3 WAYS TO TEST YOUR COLDFUSION API

Running Jasmine with Grunt Watcher

Page 68: 3 WAYS TO TEST YOUR COLDFUSION API

Running Jasmine with Grunt Watcher

Page 69: 3 WAYS TO TEST YOUR COLDFUSION API

Installing Testbox

*Install Testbox – Easy Thanks to Commandbox

*box install testbox

*Next, decide how you want to run Testbox

Page 70: 3 WAYS TO TEST YOUR COLDFUSION API

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: 3 WAYS TO TEST YOUR COLDFUSION API

Create a Test Suite// 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( '[email protected]', 'topsecret').result ).toBe('400');

});

}

}

Page 72: 3 WAYS TO TEST YOUR COLDFUSION API

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: 3 WAYS TO TEST YOUR COLDFUSION API

Running Testbox with runner.cfm

Page 74: 3 WAYS TO TEST YOUR COLDFUSION API

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: 3 WAYS TO TEST YOUR COLDFUSION API

Adding TextBox Config 1module.exports = function (grunt) {

grunt.loadNpmTasks('grunt-shell');

grunt.initConfig({ … })

}

Page 76: 3 WAYS TO TEST YOUR COLDFUSION API

Adding TextBox Config 2Watch: {

cfml: {

files: [ "cfcs/*.cfc"],

tasks: [ "testbox" ]

}

}

Page 77: 3 WAYS TO TEST YOUR COLDFUSION API

Adding TextBox Config 3shell: {

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: 3 WAYS TO TEST YOUR COLDFUSION API

Adding TextBox Config 4grunt.registerTask("testbox", [ "shell:testbox" ]);

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

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

Page 79: 3 WAYS TO TEST YOUR COLDFUSION API

Adding TextBox Config 5js: {

files: [

'js/*.js',

'specs/*.js',

"cfcs/*.cfc”

],

tasks: ['jasmine:all']

},

Page 81: 3 WAYS TO TEST YOUR COLDFUSION API

Testbox output with Grunt

Page 83: 3 WAYS TO TEST YOUR COLDFUSION API

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: 3 WAYS TO TEST YOUR COLDFUSION API

Running in Sublime Text 2

Page 85: 3 WAYS TO TEST YOUR COLDFUSION API
Page 86: 3 WAYS TO TEST YOUR COLDFUSION API