Gregs

helping me remember what I figure out

So You Want to Write Some User Journey Tests

On past projects I have relied on WebDriver to automate and write User Journey tests. This involved using either .Net or Java, which is fine, however much to my delight I was informed that there is a JavaScript version! Here’s a brief outline on how to get started with WebDriverJS and let the good times roll!

If you have node installed then you are good to go. If not, off you go ahead and install it now (you can build it from source too if you are that way inclined).

  1. open up your favourite terminal window and install selenium webdriver using npm: npm install selenium-webdriver
  2. WebDriverJS uses Mocha as it’s test runner, so go ahead and install that next: npm install -g mocha
  3. You will also need the selenium standalone server (I suggest you put the selenium stand alone server jar in a vendor folder in your project).

That’s all the software you will need, but before we get stuck in, I like to to follow a folder structure that looks a little like this:

vendor
test
    functional
        helpers
    unit

Right let’s write some code! I’ll start off with a few helper node modules (these live in test/functional/helpers) that’ll make things a little bit more re-usable. Let’s start with a helper to start the selenium server:

var assert = require('assert'),
    fs = require('fs'),
    remote = require('selenium-webdriver/remote'),
    SELENIUM = '../vendor/selenium/selenium-server-standalone-2.32.0.jar',
    server;

exports.setUpServer = function () {
    var jar = process.env.SELENIUM;
    if(!jar) {
        jar = SELENIUM;
    }
    assert.ok(!!jar, 'SELENIUM variable not set');
    assert.ok(fs.existsSync(jar), 'The specified jar does not exist: ' + jar);
    server = new remote.SeleniumServer({ jar: jar, port: 4444 });

    server.start();

    return server;
}

And now for the WebDriver helper:

var webdriver = require('selenium-webdriver');

exports.setUpWebDriver = function(server) {
    return new webdriver.Builder().
        usingServer(server.address()).
        withCapabilities({ 'browserName': 'firefox' }).
        build();
};

exports.By = webdriver.By;

This setup assumes you have FireFox installed, as it’s the simplest browser to get started with, but you can use a bunch of different ones. Now for our test, which will go to a page and assert the value of a title is correct.

var assert = require('assert'),
    test = require('selenium-webdriver/testing'),
    serverHelper = require('./helpers/server.helper'),
    webDriverHelper = require('./helpers/webdriver.helper'),
    driver,
    server;

test.before(function () {
    server = serverHelper.setUpServer();
    driver = webDriverHelper.setUpWebDriver(server);

    driver.get('http://www.google.co.uk/');
});


test.describe('Homepage view', function () {

    test.it('should have the correct title', function () {
        driver.getTitle().then(function (title) {
            assert.equal(title, 'Google');
        });
    });
});

test.after(function () {
    driver.quit();
    server.stop();
});

This file lives in your functional test folder and let’s call it test-journey.js. It basically sets up the server and driver (i.e. starts our browser and navigates to Google), before running the simple title assertion and when done, closes the browser and server. To run this test, in your terminal window inside test/functional, type:

mocha -R list test-journey.js

Keep your fingers crossed and after a few moments FireFox should fire up and run the test. If the test passed, then your console should display something like this:

. Homepage view should have the correct title: 407ms

1 test complete (13 seconds)

Pretty straightforward, no?

jQuery Dial

A long with some of my work colleagues we recently built a jQuery Dial/Knob UI control that we decided to open-source and share with others. We tested it against the following browsers:

  • Chrome 23
  • Opera 12.11
  • Safari 6.0.2
  • Firefox 17
  • IE6+

Check out the project page, for instructions on how to use it and a working example. Let me know about your thoughts or indeed any issues.

What Is a Closure in JavaScript?

If we turn to Google, invariably you are led to Stackoverflow. There’s a ton of information in that post, but I am going to try and put it into my on words so that the information sticks. A closure is a function with an inner function keyword AND you return that inner function. The inner function has access to the private member variables of the outer function. Here’s an example

 function foo(x) {
   var tmp = 3;
   return function (y) {
     alert(x + y + (++tmp));
   }
 }
 var bar = foo(2); // bar is now a closure.      
 bar(10);

But what is happening here? For starters we are creating our closure by calling bar = foo(2). So bar holds a reference to our closure, i.e. we are assigning bar a reference to our inner function. Of note as well is that the inner function is being returned from the outer function before being executed.

Now we invoke bar(10) which alerts 16, because bar() can see tmp and x. When you run bar(10) again and you get a slightly different result (i.e. 17) and that is because both x and tmp are still alive and well, and since tmp was incremented by 1 we know get 17 instead of 16. Also we have effectively we have closed over the internal variables, i.e. we can’t access tmp.

In this case our inner function here is an anonymous function, it could just as easily have been written as follows:

 function foo(x) {
   var tmp = 3;
   alertSum = function (y) {
     alert(x + y + (++tmp));
   };       
   return alertSum;
 }
 var bar = foo(2); // bar is now a closure.
 bar(10);

A closure is a special kind of object that combines both a function and the environment it was created in. The ‘environment’ refers to the local variables that are in scope, when the closure was created. In the previous example from Stackoverflow, that would be tmp and x. When the closure was created the value of tmp was 3, but then you invoked bar(10) the first time it’s value was incremented to 4. Now as described we invoked bar(10) again and got 17, because tmp now had a value of 5.

This combination of data and function, resembled key Object Oriented design construct, where it differs, is that we only work with one method here, where in OO an object has data and one or more methods that interact with the objects data.

Sometimes closures are also called function factories, because you can create new functions based on the initial value passed into the closure when you created it. Consider this example from the Mozilla article:

 function makeAdder(x) {
   return function(y) {
     return x + y;
   };
 }

 var add5 = makeAdder(5);
 var add10 = makeAdder(10);

 print(add5(2));  // 7
 print(add10(2)); // 12

It’s always helpful to come up with your own example, so how about using a closure to create a dice maker? If you have ever played Dungeon and Dragons, you need a bunch of different dice to play. Now we could use a closure as a function factory to create x number of sided dice and return a roll method that we could invoke on that die:

 function dieMaker(x) {
     var sides = x;

     function getRandomInt(min, max) {
       return Math.floor(Math.random() * (max - min + 1)) + min;
     }

     roll = function() {
         return getRandomInt(1, sides);                        
     }

     return roll;
 }​;

 var sixSidedDie = dieMaker(6);
 alert(sixSidedDie());

Backbone-Jasmine - Part 4: Display Results

Display Results

Time to display the response back to the user, to this end we’ll leverage Underscore.js built-in templating language. Our template to represent the Feed model will end up looking something like this:

<dl>
    <dt>itemId</dt>
    <dd><%= itemId %></dd>
    <dt>timestamp</dt>
    <dd><%= timestamp %></dd>
    <dt>type</dt>
    <dd><%= type %></dd>
    <dt>featOfStrength</dt>
    <dd><%= featOfStrength %></dd>
    <dt>name</dt>
    <dd><%= name %></dd>
    <dt>quantity</dt>
    <dd><%= quantity %></dd>
</dl>

We’ll use a FeedView to populate the template, so given what we know about the FeedModel and the JS template, let’s go ahead and write some tests to populate the template from the model.

describe('Feed View', function () {
    beforeEach(function() {
        loadFixtures('search-results.html');
    });

    it('should render a view item based on model values', function () {
        var feedModel = new BackboneJasmine.Feed({
                'itemId':'1',
                'timestamp':'1',
                'type': 'LOOT',
                'featOfStrength': 'Feat of Strength',
                'name': 'Name',
                'quantity': '1'
            }),
            view = new BackboneJasmine.FeedView({model:feedModel}),
            el = '';

        view.render();

        el = $(view.el).find('dl dd');

        expect($(el[0]).text()).toBe(view.model.get('itemId'));
        expect($(el[1]).text()).toBe(view.model.get('timestamp'));
        expect($(el[2]).text()).toBe(view.model.get('type'));
        expect($(el[3]).text()).toBe(view.model.get('featOfStrength'));
        expect($(el[4]).text()).toBe(view.model.get('name'));
        expect($(el[5]).text()).toBe(view.model.get('quantity'));
    });
});

To get the tests to pass we need to first create a FeedView object and it will look as follows:

var BackboneJasmine = BackboneJasmine || {};

BackboneJasmine.FeedView = Backbone.View.extend({

    tagName: 'li',
    className: 'feed',
    model: BackboneJasmine.Feed,

    render: function() {
        var variables = {
            itemId: this.model.get('itemId'),
            timestamp: this.model.get('timestamp'),
            type: this.model.get('type'),
            featOfStrength: this.model.get('featOfStrength'),
            name: this.model.get('name'),
            quantity: this.model.get('quantity')
        };

        var template = _.template($('#result-item').html(), variables);
        this.$el.html(template);
    }

});

The next step involves building out the result view, which will be bound to our collection and display multiple FeedViews. Let’s start by fleshing out the test a little to get us started

describe('Result View', function() {
    beforeEach(function() {

    });

    it('should load a fixture', function () {
        expect($('section.search-results')).toExist();
    });

    it('should display a result data', function() {

        var els = $('.search-results > ul li');
        expect($('.search-results')).not.toBeHidden();
        expect(els.length).not.toBe(0);
        expect(els.find('dl > dd').first().text()).toBe('77022');
    });
});

We can get the first test to pass easily by creating our fixture and adding it to the spec, but we’ll also need to start start pulling our result view, which will be populated with our response fixture:

beforeEach(function() {
    loadFixtures('search-results.html');
    this.response = readFixtures('feed.json');

    this.view = new BackboneJasmine.ResultView();
    this.view.collection.add(JSON.parse(this.response).feed);
    this.view.render();
});

With this in place, we can build out the ResultView object.

var BackboneJasmine = BackboneJasmine || {};

BackboneJasmine.ResultView = Backbone.View.extend({
    el: 'section.search-results',

    initialize: function() {
        _.bindAll(this, 'addFeed');

        this.collection = new BackboneJasmine.SearchCollection();
        this.$el.hide();
        this.render();
    },

    render: function() {
        this.$el.show();
        this.collection.each(this.addFeed);
    },

    addFeed: function(feed) {
        var view = new BackboneJasmine.FeedView({model: feed}),
            feedItem = view.render().el;
        this.$el.find('ul').append(feedItem);
    }

});

‘This is an idealistic view’ of syncing data between your services and your UI. Next up I’ll look at customising the collection fetch method and later on extend this to make a JSONP call.

Backbone-Jasmine: Part 3 - Search Results

In the following we will leverage Backbone Collections and the fetch feature in order to make a call to the service, receive some JSON and build up an array of models from that response. Let’s start with creating the collection tests:

describe('Search Collection', function() {

    beforeEach(function() {
        this.collection = new BackboneJasmine.SearchCollection();
    });

    it('should initialise with an empty collection', function() {
        expect(this.collection.length).toBe(0);
    });
});

Next populating the collection with some dummy data:

describe('fetch', function() {
    beforeEach(function() {
        this.server = sinon.fakeServer.create();
        this.server.respondWith('GET', '/search', [
            200,
            {"Content-Type": "application/json"},
            this.response
        ]);
    });

    afterEach(function() {
        this.server.restore();
        this.collection.reset();
    });

    it('should populate the collection', function() {

        this.collection.fetch();
        this.server.respond();

        expect(this.server.requests.length)
            .toEqual(1);
        expect(this.server.requests[0].method)
            .toEqual("GET");
        expect(this.server.requests[0].url)
            .toEqual("/search");

        expect(this.collection.length).toBe(JSON.parse(this.response).feed.length);
    })
});

Since the API is for a character’s feed, let’s call our model Feed. The call to fetch() will go away get the JSON and magically take the data and create one feed model for each feed entry returned and store it in the collection. However because the response is wrapped within a feed object, the collection object also has a parse method to handle the response and let Backbone to work the magic described previously.

var BackboneJasmine = BackboneJasmine || {};

BackboneJasmine.SearchCollection = Backbone.Collection.extend({
    model: BackboneJasmine.Feed,
    url:'search',

    parse:function (response) {
        return response.feed;
    }
});

All the code is in the ‘Search Results branch’.

Maven Example POM File to Build With Jasmine, JSlint, SASS and YUI

Since we seem to be repeating these steps time and again here’s an example of a POM file to run JSLint, Jasmine, SASS and YUI as part of the build Maven build process:
<build>
   <plugins>
        <plugin>
            <groupId>com.github.searls</groupId>
            <artifactId>jasmine-maven-plugin</artifactId>
            <version>1.2.0.0</version>
            <extensions>true</extensions>
            <executions>
                <execution>
                    <goals>
                        <goal>test</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <jsSrcDir>${project.basedir}/src/main/js</jsSrcDir>
                <jsTestSrcDir>${project.basedir}/src/test/js</jsTestSrcDir>
            </configuration>
        </plugin>

        <plugin>
            <groupId>org.jasig.maven</groupId>
            <artifactId>sass-maven-plugin</artifactId>
            <version>1.0.0</version>
            <executions>
                <execution>
                    <phase>generate-resources</phase>
                    <goals>
                        <goal>update-stylesheets</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <sassSourceDirectory>${project.basedir}/src/main</sassSourceDirectory>
                <baseOutputDirectory>${project.build.directory}/classes/assets/stylesheets</baseOutputDirectory>
                <sassOptions>
                    <cache_location>'${project.build.directory}/sass_cache'</cache_location>
                    <always_update>true</always_update>
                    <style>:compressed</style>
                </sassOptions>
            </configuration>
            <dependencies>
                <dependency>
                    <groupId>com.google.guava</groupId>
                    <artifactId>guava</artifactId>
                    <version>13.0.1</version>
                </dependency>
            </dependencies>
        </plugin>

        <plugin>
            <groupId>com.googlecode.jslint4java</groupId>
            <artifactId>jslint4java-maven-plugin</artifactId>
            <version>2.0.2</version>
            <executions>
                <execution>
                    <id>lint</id>
                    <phase>process-resources</phase>
                    <goals>
                        <goal>lint</goal>
                    </goals>
                    <configuration>
                        <failOnError>true</failOnError>
                        <sourceFolders>
                            <sourceFolder>${project.basedir}/src/main/js</sourceFolder>
                        </sourceFolders>
                        <options>
                            <predef>jQuery, $</predef>
                            <browser>true</browser>
                        </options>
                    </configuration>
                </execution>
            </executions>
        </plugin>

        <plugin>
            <groupId>net.alchim31.maven</groupId>
            <version>1.3.0</version>
            <artifactId>yuicompressor-maven-plugin</artifactId>
            <executions>
                <execution>
                    <phase>generate-resources</phase>
                    <goals>
                        <goal>compress</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <nosuffix>true</nosuffix>
                <preserveAllSemiColons>true</preserveAllSemiColons>
                <sourceDirectory>${project.basedir}/src/main/js/</sourceDirectory>
                <outputDirectory>${project.build.directory}/js-min/</outputDirectory>
                <excludes>
                    <exclude>**/vendor/*.js</exclude>
                </excludes>
                <aggregations>
                    <aggregation>
                        <insertNewLine>true</insertNewLine>
                        <output>${project.build.directory}/classes/assets/js/all.js</output>
                        <includes>
                            <include>${project.build.directory}/js-min/HelloWorld.js</include>
                        </includes>
                    </aggregation>
                </aggregations>
            </configuration>
        </plugin>
    </plugins>
</build>

Bookmarks for August 31st Through September 6th

These are my links for August 31st through September 6th:

Android - Google Chrome Remote Debugging in Two, No Three Simple Steps

Simply enabling Open Chrome on your phone → Settings → Developer Tools → check Enable USB Web debugging and running the following command:
adb forward tcp:9222 localabstract:chrome_devtools_remote
are not quite enough to be able to bring up the Remote Webkit inspector. Remember to also check the box for Settings → Developer options → USB debugging and then you are good to go!