a framework for mocking ajax requests with jQuery and jsHamcrest
h1. MockAjax
MockAjax is an mock XMLHttpRequest implemetation designed to allow asyncronous xhr requests to be run inside a synchronous testing framework. It is specifically designed to run with JsHamcrest and jQuery or PrototypeJS and inside many different unit testing frameworks. Includes support for jsTestDriver, jasmine, JsUnitTest, jsUnity, QUnit, Rhino, YIUTest and screwunit
h2. So what does it do?
h2. Show me
pre. // fetch an article from the server and put it in a div on the page // set up our expectations (you can think of this as a virtual server) whenRequest({ url: is("/api/article/1") }) .thenRespond({ type: "html", data: "Article Titlethis is the body" }); // run the code under test jQuery.ajax( { url: "/api/article/1", success: function(article) { $("#doc").html(article); } } ); // trigger the respond from the server MockAjax.respond(); // assert that the test ran correctly assertThat($("#doc > H2").text(), equalTo("Article Title"), "title correctly applied to article heading"); assertThat($("#doc > P").text(), equalTo("this is the body"), "body correctly applied to article paragraph");
h2. How do I use it
First of all, this framework requires JsHamcrest (or something which uses hamcrest compatible matchers), so you need to be using that. Right after the JsHamcrest.Integration is preformed, add this line
@MockAjax.Integration.JsTestDriver().jQuery();@
Integration commands can be chained and the following frameworks are supported
h3. testing frameworks
h3. development frameworks
h2. I want more
h3. whenRequest
Each parameter to @whenRequest@ takes a "hamcrest matcher":http://jshamcrest.destaquenet.com/modules/matchers.html . All parameters are optional You can create as many whenRequest / thenRespond pairs as you like
examples:
pre. whenRequest({ method: anyOf("PUT", "DELETE"), url: matches(/blog/\d+/), username: is("admin") })
h3. thenRespond
Each parameter passed to @thenRespond@ constitutes some part of the response from the server
By default there is one built in response that will be returned if no request matches and that is a 404 - file not found response
examples:
pre. thenRespond({ type: "html", data: "hello world" }); thenRespond({ data: '{"this":"is","some":["json","data"]}' }); thenRespond({ status: 500, data: "Fatal error: Call to undefined function: mysql_set_charset() in /pages/includes/comments.php on line 152" }); thenRespond({ status: 401, headers: { "WWW-Authenticate": "Basic realm="intranet"" }, data: "401 - Unauthorized" });
h3. Dynamic Responses
Rather than specifying the responses for each request statically, you can define a callback function which returns an object containing any response parameters. The callback is provided with the original request.
examples:
pre. // echo the request method and url back as the response thenRespond( function( req ) { return { type: "text", data: ( req.method + req.url ) } } );
pre. // modify the body based on the url parameter thenRespond( function (req) { return { type: "text", data: "hello " + req.url.match(/?name=(\w+)/ ) } } );
pre. // set the last modified header to right now thenRespond( function (req) { return { headers: { "Last-Modified" : (new Date()).toUTCString() } } } );
h2. MockAjax.respond()
the @respond@ method causes MockAjax to respond with the oldest ajax response in it's queue. The respond method is not required for synchronous ajax requests. if there are multiple inflight requests at once, you can cause responses to return out-of-order by passing a non-zero number. @MockAjax.respond(3)@ causes the fourth queued response to be returned
h2. MockAjax.respondAll()
the @respondAll@ method causes MockAjax to respond to all of the ajax responses in it's queue, oldest first. The respondAll method is not required for synchronous ajax requests.
h2. MockAjax.timeout()
the @timeout@ method causes any queued calls to setTimeout to be executed. All calls to setTimeout are trapped, so if you have multiple timers you will need to call timeout multiple times to clear them all.
pre. // example testing the timeout feature of some code // fire off the ajax request jQuery.ajax( { url: "/article/12", timeout: 60000, // one minute (but we don't want wait that long for the test!) success: this.doSomething, error: function(x, e) { if(e === "timeout") { $("#doc").text("Error: the request timed out. Perhaps the server died"); } } } ); // force the timeout MockAjax.timeout(); // assert that the test ran correctly assertThat( $("#doc").text(), contains("timed out"), "testing request timeout handling");