Testing Asynchronous JavaScript with Jasmine

For many applications, when you call a method the effect is immediate. For example calling a method like showErrorMessage(“Error”) will instantly update the page to give feedback to the user.

However, not all applications are this simple. Caplin uses StreamLink libraries to connect our clients to servers running Caplin Liberator. These StreamLink libraries receive messages asynchronously to the initial request so that if you request a financial instrument from the server, it will be returned at an indeterminate (but definitely short!) time in the future and will be handled after the current event has finished executing.

Before we can discuss the difficulties of testing asynchronously in JavaScript we first have to consider the way that JavaScript threading works. For simplicities sake I will not be discussing WebWorkers, which are still a long way from deployment. See my previous post Why Web Workers don’t work (yet).

JavaScript is single threaded*, that is to say, only a single thread can execute any one time.  Once an event starts running on the single execution thread it will continue running until it comes to an end and it surrenders to the next event. So even if a new event occurs (button click, XHR Callback, setTimeout callback etc) while it is running, that event won’t be handled until the current event has completely finished. See this great post if you are interested in the specifics of how the threads work.

In addition, there is no sleep method in JavaScript. The decision to not include a sleep method in JavaScript is one of the greatest decisions in history of great decisions. Seriously. On the level of the guy who thought “Hey, why don’t we slice that before we sell it?”.

If you wish to delay processing you must use the setTimeout() method to break the functionality to be called onafter the current event has finished executing. The point being that no matter how long your current work takes to run, the delayed action will not take place until after it is finished.
Because of this testing asynchronous functionality with a typical testing tool such as JSUnit or JSTestDriver can be difficult, as their tests run in a single thread and cannot wait for events to occur.

However, there are various technologies which excel at providing testing functionality for asynchronous testing. I have recently used both QUnit and Jasmine, both of which have been excellent at this job. At Caplin we use Jasmine, and so I will be focusing on that.

Let us consider  a very simple example. Here the code uses a setTimeout to set a variable one second after being called, although in reality you are more likely to have something like an XHR callback.

var x;
function asyncMethod() {
setTimeout(function() {
x = 5;
}, 1000);
};

Now if there was no callback one could trivialy test this as follows

asyncMethod();
assertEqual(5, x);

However, this is obviously not possible as “x” will not change for 1 second so is undefined when we call the assert method. Fortunately Jasmine gives us the tools that we need to deal with this.

Jasmine provides two methods. A runs() method and a waitsFor() method to allow you to write asynchronous tests. This allows you to write tests which will be invoked on specific events, rather than as part of a single event.
Let us take a look at a look at example of how this would work:

describe('Asynchronous Methods', function() {
it("sets up latency configuration and receives latency",
function() {
runs(function() {
asyncMethod();
}, "an asynchronous method");
waitsFor(function() {
return x == 5;
}, "x to be set to 5", 5000);
});
});

So what is happening here? Let us break down our test. We call runs() passing it a function to call asyncMethod(). We then call waitsFor() giving it a function to return whether x == 5.

Jasmine will call the runs() and waitFor() methods in the order you passed them. As soon as it gets to a waitFor() method it will call it every 10 ms until it returns true and then it will continue onto the next runs() method and so on.

So in this test Jasmine calls the first run() function which calls the asyncMethod(). It then calls waitsFor() which will return false as the value of x is still 1. Jasmine will keep calling waitsFor() every 10ms until 1 second later the asyncMethod callback is invoked and x is set to 5. 10ms later waitsFor() will be invoked again and return true and the test will pass.
With this approach we can test asynchronous functionality in a simple and powerful way. This has been very useful as it allows us to replace a lot of what we used to use Selenium for with Jasmine.

Footnote: There are certain times in which the browser can interrupt the current thread to do other things. However, these are uncommon and usually side effects of uncommon events (The screen being resized, fields with an onBlur method losing focus) and we can assume that these cannot be used for hacking in tests.

Leave a Reply

Your e-mail address will not be published. Required fields are marked *