vilimblog tech and food

19Jan/116

Testing REST API with LearnBoost’s Tobi + Vows.js

I've been looking for a clean, framework-independent way of doing white-box API testing using Node.js. For a long while, the things that popped up when doing a quick scan of the Node Package Manager package lists weren't ticking all of the right boxes: zombie.js is going for a full browser simulation but doesn't provide a simple Browser.post() method (you have to use selectors to find the form.submit button and then fire a click() on it), Node's native http.Client is too low-level and doesn't do cookies, and various other http request wrappers weren't quite cutting it either. I think I've found a solution for this particular version of the problem: LearnBoost's Tobi combined with Vows.js is letting me do clean REST API testing, with a minimum of hassle, and all the built-in sugar-coated goodness of should.js fluent assertions. For example:
var HOST  = 'localhost';
var PORT  = 80;
var API   = "/api/user/create";

var tobi   = require('tobi'),
    vows   = require('vows'),
    assert = require('assert');

var newbie = function() { return tobi.createBrowser(PORT, HOST) };

// -------------------------------------------------------------
// Macros.
// -------------------------------------------------------------

var macroPostOnlyApiChecks = function(url) {
	return {
		'GET': {
			topic: function() {
				var browser = newbie();
				browser.get(url, this.callback);
			},
			'should fail.': function(res, $) {
				res.should.not.have.status(200);
			}
		},
		'Empty POST': {
			topic: function() {
				var browser = newbie();
				browser.post(url, this.callback);
			},
			'should fail.': function(res, $) {
				res.should.not.have.status(200);
			}
		}
	};
};

var macroCreateUserOk = function(suite) {
	return {
		'Create user': {
			topic: function() {
				var browser = newbie();
				var data = { signupUsername: '', };
				
				data = JSON.stringify(data);
				browser.post('/api/user/create', data, this.callback);
			},
			'should succeed.': function(res, $) {
				res.should.have.status(200);
				console.log(res);
				
				// Pass created user credentials back to
				// the calling suite for later use.
				if (suite) {
				}
			}
		}
	};
};

// -------------------------------------------------------------
// User API Test Suite
//
// Run me with: vows api.user.create.vows.js --spec
// -------------------------------------------------------------

var suite = vows.describe('User API Test Suite');

// Batches  are executed sequentially.
// Contexts are executed in parallel.

suite.addBatch(macroPostOnlyApiChecks(API));
suite.addBatch(macroCreateUserOk());

suite.export(module);
Future steps then include using the macros to help set up other tests that require a valid user, etc. Overall, this is the most straightforward solution I've yet found for the problem of testing a REST API while also faking a session.
Filed under: JavaScript, Tech 6 Comments
17Dec/100

Javascript Decorators

One reason why Javascript rocks:

As a use case, imagine you want to restrict a certain set of functions to only run if you are logged in. Doing stuff like this is ridiculously easy with first-class functions.

Here's a generic decoration example:

(function()
{
	var original = function(paramOne, paramTwo) {
		console.log('original: ' + paramOne + ' ' + paramTwo);
	};

	var decorator = function(originalFn) {
		return function(paramOne, paramTwo) {
			console.log('decorator: ' + paramOne + ' ' + paramTwo);
			originalFn(paramOne, paramTwo);
		};
	};

	var fnTable = {};
	var registerCallback = function(url, callback) {
		fnTable[url] = callback;
	};

	registerCallback('/abc', original);
	registerCallback('/def', decorator(original));

	fnTable['/abc']('one', 'two');
	fnTable['/def']('three', 'four');
})();

Update: Here's the specific way you'd do this with node.js/Express:

Since all of the URL handlers you register have the same signature, it's easy to add precondition checks to the handlers via decorators.

var decorator = function(originalFn) {
    return function(req, res) {
        if (req.session.userLoggedIn) {
            originalFn(req, res);
        } else {
            redirectSomewhere(res);
        }
    }
}

app.get('/url', decorator(original));
app.post('/other', decorator(original));

You get preconditions essentially for free, which is a damn sight better than adding the if() block to each and every handler function. Also, if you need more preconditions in the future, you can just stack them.

Filed under: JavaScript, Tech No Comments
26Sep/100

Geocoding with Mapstraction v2 and Google Maps v3

There isn't a whole lot of info out there about how to use Mapstraction, especially the v2 API, so here's one thing I've had to pick up along the way.

I spent a little time hacking together a geocoder module that uses the Google Maps v3 API, but was having to track down why it wasn't loading properly.