Blog

17
June 2015

Gavin Pickin

Cordova Hooks - Deep Dive into my Jasmine Unit Test Hook written in NodeJS

Android, Cordova / Phonegap, Dev.Objective, Ionic, IOS, Javascript, Mobile Development

After my session about Dev.Objective() called “Getting your Hooks into Cordova - Workflows and Build Helpers”, I decided I should do a deep dive into each of my Hooks I discussed in my presentation, and today we’ll look at my Jasmine Unit Tests Hook, written in NodeJS.

My presentation mini site is available here on my blog at http://www.gpickin.com/devobj2015/cordovahooks/

As I add more blog posts about this topic, I will continue to update the mini site.
The minisite has links to the gists of all the hooks I had, the slides and links to the few blog posts I did find on the topic.

For more information on what hooks are, please refer to my Session Review and Overview here: Post Conference - Session Review - Getting your hooks into Cordova- Workflows and Build Helpers

The next hook in my hybrid mobile workflow with Cordova, is my Jasmine Unit Test hook.

This hook is setup to run in the BEFORE_BUILD.

Unit testing is one of those Development Lifecycle phases that most people are aware of, majority of people know why they should do it, and fewer people actually do it. I believe there is a invisible barrier that most people hit when deciding to implement Unit Testing, and the main reasons are it is time consuming and because people think its hard. Running your unit tests automatically, takes a lot of the “HARD” and “TIME” out of it.

My other session I gave at dev.Objective() was “How do I write Testable Javascript” so Unit Testing is up my alley you might say. Our organization fights some of those same battles, but with Cordova and Hooks, I saw the perfect opportunity to automate the unit tests on every build. Link to that presentation below.

I searched and searched, but I couldn’t find any existing Jasmine Hooks out in the world already, so I decided it would be a good idea to dive into Node, and try and figure out how it all works and write my own. 

Was that a good idea? 
At first, I thought it was, then for a while, I was unsure, but eventually, I got it to work, and I felt so much better about getting my hands dirty, diving down the rabbit hole that is NodeJS.

There are essentially 2 ways to run Jasmine, Jasmine which is the Node version, and then Jasmine with PhantomJS, a headless browser to give you access to a DOM etc, which is required for DOM related tests etc. 

This hook is setup with Node Jasmine, so its perfect for Service level objects, testing business logic. If you want to test more, you would need the PhantomJS version, which I will have to write later and add as a follow up post.

First I installed the NPM module
#: npm install Jasmine

That creates a folder called node_modules, and installs Jasmine, and its dependencies, if you open that folder up, it should look something like this.

SCREENSHOT 2

Then you have to init Jasmine to create the folder structure which has the spec files, and the config file, to tell Jasmine what files to process.
#: jasmine init

SCREENSHOT 1

As you can see from the sceenshot, which includes a support folder, than has the Jasmine.json. This is what it has in it

{
    "spec_dir": "spec",
    "spec_files": [
         "**/*[sS]pec.js"
    ],
    "helpers": [
        "helpers/**/*.js"
    ]
}

 

Jasmine’s convention is that it runs any JS file in the spec directory (or subdirectory) that has the suffix spec.js or Spec.js. You can also place helpers in the helper folder, or any of its subdirectories. You can edit this as needed, and then when you run Jasmine from the CLI, or through Cordova as a hook, it will run the files specified in this file.

Next, I tracked down how the module actually was run, copied the code into a hook, and edited it, until I managed to successfully pass all the right information, and have Jasmine output the information I needed. Lucky for you, I figured that all out, so here is the code, below.

https://gist.github.com/gpickin/e6ce95a1d1bf7c98da30

#!/usr/bin/env node
 
var path = require('path');
var Command = require('../../node_modules/jasmine/lib/command.js');
var Jasmine = require('../../node_modules/jasmine/lib/jasmine.js');
var thePath = path.join( path.resolve(), 'www');
 
var jasmine = new Jasmine({ projectBaseDir: thePath });
var examplesDir = path.join(__dirname, '..', 'node_modules', 'jasmine-core', 'lib', 'jasmine-core', 'example', 'node_example');
var command = new Command(thePath);
 
console.log('Running Jasmine Tests');
command.run(jasmine, [] );

 

Lets break it down.

#!/usr/bin/env node

Cordova Hooks always need to know what type of Hook it is, so this tells Cordova its a node hook.

 

var path = require('path');

Require the path module, so we can use it for folder / path functions.

 

var Command = require('../../node_modules/jasmine/lib/command.js');

Require the command module, so we can run the Jasmine core from the command line. This takes care of input, output to the console etc.

 

var Jasmine = require('../../node_modules/jasmine/lib/jasmine.js');

Require the Jasmine core module… this does all the hard work.

 

var thePath = path.join( path.resolve(), 'www');

We set a variable with the path to where our BASE folder should be, in cordova projects, its inside the WWW folder.

 

var jasmine = new Jasmine({ projectBaseDir: thePath });

Next we initialize a new jasmine runner, passing in the project base path.

 

var examplesDir = path.join(__dirname, '..', 'node_modules', 'jasmine-core', 'lib', 'jasmine-core', 'example', 'node_example');

I assume this is defining the examples directory, not sure why I left this in here, if its not necessary. I should remove it and see what happens

 

var command = new Command(thePath);

Create a new Command Runner.

 

command.run(jasmine, [] );

Run jasmine with the command runner.

Not too complicated, but there was a few little things I had to tweak to get it working.
Now, all you have to do is install Jasmine, copy this into a hook file in the before_prepare folder, make it executable, and then every time you run build, it will run.

After my JS Hint hook has fired, and succeeded, then my Unit Tests run with Jasmine. If the unit tests all pass, the build continues, otherwise the process ends, with relevantfailing test information. Success or failure, Jasmine outputs number of specs, execution time etc.

Failures

Running command: /Users/gavinpickin/Dropbox/Apps/myapp/hooks/before_prepare/05_jasmine.js /Users/gavinpickin/Dropbox/Apps/myapp
/Users/gavinpickin/Dropbox/Apps/cstctraining/www
Running Jasmine Tests
/Users/gavinpickin/Dropbox/Apps/myapp/www/spec/attendeeServiceSpec.js

Started
FFFFF

Failures:
1) Player should be able to play a Song
  Message:
    ReferenceError: newAttendeeService is not defined
  Stack:
    ReferenceError: newAttendeeService is not defined
        at Object.<anonymous> (/Users/gavinpickin/Dropbox/Apps/myapp/www/spec/attendeeServiceSpec.js:6:23)
  Message:
    ReferenceError: player is not defined
  Stack:
    ReferenceError: player is not defined

 

Success

Running command: /Users/gavinpickin/Dropbox/Apps/myapp/hooks/before_prepare/05_jasmine.js /Users/gavinpickin/Dropbox/Apps/myapp
/Users/gavinpickin/Dropbox/Apps/myapp/www

Running Jasmine Tests
/Users/gavinpickin/Dropbox/Apps/myapp/www/spec/loginServiceSpec.js

Started
…

3 specs, 0 failures
Finished in 0.006 seconds


I need to submit this hook/module to the nom registry, I will post about that process when I do that.
I will also write a hook for a PhantomJS version, to allow you to write tests that call browser DOM or interact with libraries like jQuery.

Link to the gist on github: https://gist.github.com/gpickin/e6ce95a1d1bf7c98da30
More information on Jasmine: http://jasmine.github.io/
Link to my Cordova Hooks mini site http://www.gpickin.com/devobj2015/cordovahooks/
Link to my “How do I write Testable Javascript” Presentation Mini Site http://www.gpickin.com/devobj2015/testablejavascript/

Blog Search