Tag: Javascript

Switching to Chrome for development

Just a quick post to mention that I’ve switched from Firefox to Google Chrome 4 (still in beta) as my development browser on Windows. Several clientside developers recommend using Safari (on Mac) as your main development platform and whenever I’m using my Mac, I’ve seen the wisdom in that. However, on Windows, Safari has two major issues that annoy the hell out of me:
1. It crashes too often
2. It opens external links in a new window instead of a new tab

Chrome doesn’t have these issues and it also uses the Webkit rendering engine. It’s also the fastest browser available on Windows.

The biggest problem with Chrome 3 is that it doesn’t allow adding new style rules through the developer tools. Chrome 4 has updated to a newer version of the Webkit Inspector, which does offer this functionality (and several other new stuff), so all in all, Chrome seems to have become the best development environment on Windows with the latest installment.

It does take some getting used to when you’re used to working with Firebug for years, but hey, change is good, according to Obama.

Test Driven Development & Javascript- A real world example

Test Driven Design (TDD) is an interesting programming technique, but it’s often hard to do with Javascript, because of the strong ties between javascript and user interaction. This makes the line between unittesting and functional testing blurry at best. However, it’s still a good idea to run unit tests on your code, because it adds an extra layer of fidelity to your code.

This article uses a paginator as a real world example to develop using TDD. Although a paginator has a UI component, the interactions can be abstracted from the actual functioning of the code. Simply put, clicking “next page” should do nothing more than call a method of the paginator that changes its state to reflect that the next page has become the current page.

What exactly is a paginator?
A paginator is an object to track the state of a data set that has been divided into pages. This data set can vary from search results to a collection of pictures or pages of text. If the state changes, the paginator somehow notifies other objects of this change, so that they might respond, e.g. load the new data.

So what kind of specifications does this paginator have?
To keep things simple for now, the paginator offers only navigation to the first, last, next and previous pages.
There’s also the basic requirement of keeping track of the state of the pagination and returning this state when required.
The paginator also needs to have some way of informing other objects of any change.

Now that we’ve made up some requirements, we can start building the unit tests. The tests are built using the new Evidence.js unit test framework by Tobie Langel. At a conceptual level, this doesn’t differ much from other testing frameworks, such as QUnit or JSUnit.

Starting a unit test is as simple as this:

var PaginatorTest = Evidence.TestCase.extend("PaginatorTest", {
	"setUp": function () {
		this.state = {
			"currentPage": 1,
			"itemsPerPage": 10,
			"totalItems": 200
		}
		this.state2 = this.state;
		this.p = new Paginator(this.state);
	},
	"tearDown": function () {
		delete this.state;
		delete this.p;
	},
	// add tests here
})

the setUp method is called before each test and the tearDown method is called after each test. This ensures that it’s not necessary to repeat code for every test and that each test can start in a clean environment (so no pollution from previous tests).

These few lines of code already imply a few things:
1. the paginator is created by calling new Paginator(state)
2. the parameter for a new paginator object is an object containing the initial state.

So let’s create the code that allows this:

Paginator = Class.create({
	initialize: function (state) {
		// initialize paginator code
	}
})

This is prototype.js based code, but of course, all of this is also possible in other frameworks, or even in plain javascript.

Time to write the first unit test. After initialization, the paginator instance should at least be able to return the state, so let’s write a test for that.

	"testPaginatorInitsCorrectly": function () {
		this.assertIn("getState", this.p);
		var state = this.p.getState();
		this.assertEqual(state.currentPage, this.state.currentPage);
		this.assertEqual(state.itemsPerPage, this.state.itemsPerPage);
		this.assertEqual(state.totalItems, this.state.totalItems);
	}

The first test is to assure that the paginator instance has a getState method. This prevents the test from failing on the second line if getState hasn’t been implemented.
To get this test to succeed, the Paginator class is changed to the following:

Paginator = Class.create({
	_DEFAULT_STATE: {
		currentPage: 1,
		itemsPerPage: 10,
		totalItems: 0
	},
	_state: null,
	initialize: function (state) {
		if (typeof(state) != "object") {
			throw "Paginator: state is not an object.";
		}	
		this._state = Object.extend({}, this._DEFAULT_STATE);
		for (var i in state) {
			if (this._state.hasOwnProperty(i)) {
				this._state[i] = state[i];
			}
		}
	},
	getState: function () {
		return this._state;
	}
})

This code has the expected result of passing the test. The initialize method checks if the state argument is an object, and if so it copies its properties to the _state property, as long as they’re already part of that object.
This shows one major point of TDD: don’t write more code than you need. At the moment, it’s not needed to abstract the for loop into a separate method, but later on, this might change. Don’t anticipate for that by already creating this method, but leave the code as it is.

The biggest role of the paginator is to make changes to its _state property based on user actions. Although it’s possible to write one test for navigating to first, last, next and previous pages, it’s probably better to test each of those cases separately.
To accomodate the first and previous navigation, we’ll create a second Paginator in the setUp code that initializes with the current page set to 5.

"testNavigateFirstPage": function () {
	this.assertIn("gotoFirstPage", this.p2);
	this.p2.gotoFirstPage();
	var state = this.p2.getState();
	this.assertEqual(1, state.currentPage);
},
"testNavigatePreviousPage": function () {
	this.assertIn("gotoPrevPage", this.p2);
	this.p2.gotoPrevPage();
	var state = this.p2.getState();
	this.assertEqual(this.state2.currentPage-1, state.currentPage);
},
"testNavigateNextPage": function () {
	this.assertIn("gotoPrevPage", this.p);
	this.p.gotoPrevPage();
	var state = this.p.getState();
	this.assertEqual(this.state.currentPage+1, state.currentPage);
},
"testNavigateLastPage": function () {
	this.assertIn("gotoLastPage", this.p);
	this.p.gotoLastPage();
	var state = this.p.getState(), 
		lastPage = Math.ceil(this.state.totalItems/this.state.itemsPerPage),
		lastPage2 = Math.ceil(state.totalItems/state.itemsPerPage);
	this.assertEqual(lastPage,lastPage2);
}

And this is the updated code for the Paginator class:

var Paginator = Class.create({
	"_DEFAULT_STATE": {
		"currentPage": 1,
		"itemsPerPage": 10,
		"totalItems": 10
	},
	"_state": null,
	"_totalPages": 1,
	"initialize": function (state) {
		if (typeof(state) != "object") {
			throw "Paginator: state is not an object.";
		}
		this._state = Object.extend({}, this._DEFAULT_STATE);
		this.setState(state);
	 	this._totalPages = Math.ceil(this._state.totalItems/this._state.itemsPerPage);		
	},
	"getState": function () {
		return this._state;
	},
	"gotoNextPage": function () {
		if (this._state.currentPage <= this._totalPages) {
			this.setState({
				"currentPage": (this._state.currentPage+1)
			});
		}
		return this._state;
	},
	"gotoPrevPage": function () {
		if (this._state.currentPage >= 1) {
			this.setState({
				"currentPage": (this._state.currentPage-1)
			});
		}
		return this._state;		
	},
	"gotoFirstPage": function () {
		this.setState({"currentPage": 1});
		return this._state;
	},
	"gotoLastPage": function () {
		this.setState({"currentPage":this._totalPages});
		return this._state;
	},
	"setState": function (newState) {
		for (var i in newState) {
			if (this._state.hasOwnProperty(i)) {
				this._state[i] = newState[i];
			}
		}
	}
})

First thing that can be noticed is that now there’s a setState method, containing the for loop that was previously in the initialize method. By abstracting that bit of code to a separate method, it has become possible to re-use that code in the navigation methods.
Technically the “don’t write more code than you need to” rule is broken here, as it was also possible to change the currentPage property directly. However, this extra level of abstraction makes the code easier to understand.

So now there’s only one thing left: making sure that the Paginator instance triggers an event when the state changes. So let’s add a test for that:

	"testStateChangeEvent": function () {
		document.observe("pag:paginator_change", function (e) {
			this.assertEqual(e.memo.state.currentPage, this.state.currentPage+1);
		});
		this.p.gotoNextPage();
	}

Adding a custom event (prototype.js supports this in an easy way) will make this easy:

"setState": function (newState) {
	for (var i in newState) {
		if (this._state.hasOwnProperty(i)) {
			this._state[i] = newState[i];
		}
	}
	document.fire("pag:paginator_change", {"state": this._state});
}

So that’s a basic real world example of how to do Test Driven Development. A later article will come back to this and add some more functionality and take a look at how to use Test Driven Development when there are UI components involved.