Jasmine and testing asynchronous callbacks

2011/02/28 § Leave a comment

I have been learning a variety of scripting, or dynamic, or web-development languages, simply because I am tired of being mired in C++ tar day in and day out. I am impressed — as I should be — at how much more seamless the interface is between code, editor software, and editor — but that is something I am still not completely familiar with and this shall discuss later.

My first attempt at a project is a MetaFilter reader for HP’s WebOS. The phone’s SDK has you write applications in Javascript, which on the surface is a completely whacked proposition, and I must infer that the process is incredibly mind-stretching (and even fun!.) So far, this has proven true. Javascript is a helluva drug, but there’s plenty of literature on this and anything I write I’ll get to later.

I’ve been using Jasmine to unit test my application. As a Behaviour-Driven-Development (i.e. write use cases first, but focus on human terms over code assertions) framework, it has been straightforward to adopt. However, the way one uses it for asynchronous callbacks was not entirely clear. While the user guide does go into the intended strategy at length, it took me a while to clue in that I have to structure the entire test-flow within runs, waits and waitsFor blocks.

Whereas I thought I could get away with the following:

describe('Some asynchronous code', function() {

	beforeEach(function() {
		//code to set up structures
	});

	it('should pass test 1, which doesn't actually care about the async code.') {
		//code to perform test
	});

	it('should pass test 2, which does require some asyncronous calls to be checked') {
		runs(function() {
			//Perform code that dispatches asynchronous callbacks
		});
		waits(500); //Could make this tighter with the more complicated waitsFor function.
		expect(something).toHaveHappened();
		expect(something_else).toLookLikeThis();
	}

	afterEach(function() {
		//code to clean up stuff
	});
});

I wound up having to massage it as follows:

describe('Some asynchronous code', function() {

	beforeEach(function() {
		runs(function () {
			//code to set up structures
		}
		waitsFor(...) // arguments check that the above stuff happened.
	});

	it('should pass test 1, which doesn't actually care about the async code.') {
		runs(function() {
			//code to perform test
		}
		waitsFor(...) // arguments check that the above stuff happened even though I don't really care.
	});

	it('should pass test 2, which does require some asyncronous calls to be checked') {
		runs(function() {
			//Perform code that dispatches asynchronous callbacks
		});
		waits(500); //Could make this tighter with the more complicated waitsFor function.
		runs(function() {
			expect(something).toHaveHappened();
			expect(something_else).toLookLikeThis();
		});
	}

	afterEach(function() {
		runs(function() {
			//code to clean up stuff
		});
		waitsFor(...) //arguments check whether the above thing happened.
	});
});

The reason for this is because, as mentioned in the guide, all of the functions in the runs() functions happen in linear order. As such, I had to use it (along the waits() and waitsFor() blocks) to force the entire test code to run linearly. The waitsFor() and waits() then become gates that only allow the following runs() fragments to go on when the waits() times out or the waitsFor() passes the check I put in asking if the previous code has run to completion.

Even if I don’t care about testing the results of those calls, in my test I care about having them complete so everything is ready by the time I check my expectations on the results. Thus, I have to linearize the entire test anyway. If I *don’t* place some code in a runs() block, it happens immediately with no regard for what happened previously. Even though I had that waits() function in test 2, I’m not sure whether code outside of runs() actually is forced to wait or whether it just continues on blithely.

In its defense, all the examples on the user guide are completely wrapped in runs() and waitsFor() calls. However, it was only implied that the entire process must be shepherded this way. It is entirely possible that my difficulty comes from asynchronous calls being new to me; in C++, I very rarely find myself in a world where I simply do not proceed until the function I am calling has returned a definite value … when we do allow the called function to go off and finish its work independently, I’m usually warned by tonnes of complicated thread/process code. Because the WebOS Javascript library has wrapped all of this for me so nicely, not only am I stuck in unfamiliar ground but I also don’t recognize more subtle warning signs.

Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

What’s this?

You are currently reading Jasmine and testing asynchronous callbacks at The Lambda Calculus.

meta

%d bloggers like this: