
JavaScript testing – Requests
WARNING
Legacy article - Project is inactive and not likely to become active. Code might be outdated.
When testing JavaScript you might have run into the problem of testing requests, such as XmlHttpRequests
(XHR).
You might also have run into a way of testing it, such as the one provided by Buster.js (which in turn uses Sinon.js).
These tools are useful, if you haven’t tried them yet go do it.
One problem though — a unit test is supposed to be as implementation independent as possible, and testing XHR is rather tightly bound to the implementation. If I suddenly start requesting resources by adding script tags to the dom my tests will fail even though the solution is solid. We don’t want that.
To solve the challenge I’ve considered multiple strategies, one of them ended up with me writing a small addition to the Buster.js testing framework.
An assert.urlRequested(server, url[, message])
that basically takes an fakeServer and a URL to test if a request have been made to a certain URL.
The additions are called RfBusterAssertions.
buster.testCase("Test assertions", {
setUp: function() {
this.server = this.useFakeServer();
},
"An simple XHR test": function() {
var request = new XMLHttpRequest();
request.open('GET', "foo.js", false);
request.send();
assert.urlRequest(this.server, "foo.js");
expect(this.server).toHaveRequestedUrl("foo.js");
},
"An simple script test": function() {
var script = document.createElement('script');
script.type = "text/javascript";
script.src = "foo.js";
document.head.appendChild(script)
assert.urlRequest(this.server, "foo.js");
expect(this.server).toHaveRequestedUrl("foo.js");
}
});
What is basically does is to run through all script-tags on the page and check the src-argument for the specified URL and to run through all the registered requests on the server and check for their URLs. This covers the normal ways to load resources. Unfortunately, this still won’t handle things like loading through Flash, Java Applets and different other resources, if you do that, you’ll probably have to write the test yourself.
Another strategy I’ve considered (and have used) is by having a URL property on the object I’m testing and then setting it to a local JavaScript file that launches a predefined callback. This has the advantage of both testing the configuration option and being completely independent. The disadvantage is that, if I don’t use XHR I have no way to test the get parameters, also this adds details that can cause the test to fail.
/*
The contents of js-file should be:
window.testCb();
*/
buster.testCase("Callback", {
setUp: function() {
// Note when having a fake server you have to manipulate the
// result to contain the script in the comment above.
this.server = this.useFakeServer();
},
"An simple callback test": function(done) {
window.testCb = function() {
// There's probably a better, way this is just
// to ensure an assertion that will be true no matter what
assert.equals(true, true);
done()
}
var script = document.createElement("script");
script.type = "text/javascript";
script.src = "callback.js";
document.head.appendChild(script);
}
});
The last way of testing that I have considered but completely discarded is good on the technical part, but will most likely end up too impractical. It would be possible to write a server component that answers to a callback, but add all the metadata as a param to the callback. Thus, it would be possible to test things in the callback such as get parameters. This I deemed too impractical due to the amount of setup needed, and honestly I think it’s better to have a decent test that’s used rather than a perfect test that ain’t.
Special thanks to Christian Johansen for all his help on these tests.