问题
I have a dynamic page built with jQuery. Html pieces are loaded from mustache templates. These templates are downloaded from a url, and I would like to unit test the overall html construction :
The JsTestDriver test is :
AppTest = TestCase("AppTest")
AppTest.prototype.test = function() {
var actualHtml = "";
getHtml({ "title": "title", "header": "header", "text": "text", "authors": [ {"firstname": "firstname", "lastname": "lastname"} ] }, function(html) {
actualHtml = html;
});
assertEquals("expected html", actualHtml);
};
And the code :
function getHtml(json, resultFunc) {
jQuery.ajax({
url: "url/to/mustache/template",
success: function(view) {
resultFunc(mergeArticleModelAndView(json, view));
},
error: function(jqXHR, textStatus, errorThrown) {
resultFunc(textStatus + " errorThrown: " + errorThrown);
},
dataType: 'text',
async: false
});
}
Then I launch the tests and the result is :
$ java -jar JsTestDriver-1.3.2.jar --port 9876 --browser /usr/bin/firefox --tests all
F
Total 1 tests (Passed: 0; Fails: 1; Errors: 0) (8,00 ms)
Firefox 5.0 Linux: Run 1 tests (Passed: 0; Fails: 1; Errors 0) (8,00 ms)
AppTest.test failed (8,00 ms): AssertError: expected "expected html" but was "error errorThrown: [Exception... \"Component returned failure code: 0x80004005 (NS_ERROR_FAILURE)\" nsresult: \"0x80004005 (NS_ERROR_FAILURE)\" location: \"JS frame :: http://localhost:9876/test/main/js/jquery.min.js :: <TOP_LEVEL> :: line 16\" data: no]"
()@http://localhost:9876/test/test/js/app_test.js:25
So the error callback has been called, and I don't understand why it breaks with JsTestDriver, and the code works when calling the application manually with a browser
Last thing, the jsTestDriver.conf :
server: http://localhost:9876
load:
- test/js/app_test.js
- main/js/jquery.min.js
- main/js/jquery.mustache.js
- main/js/app.js
Thank you for your advices. More generally, what unit test frameworks do you use for javascript and command line testing with DOM and jQuery ?
回答1:
An alternative would be to get JsTestDriver to serve your template and some test JSON data. The configuration below allows you to put static JSON test data (e.g. my_data.json) in src/test/webapp/json and Mustache templates in src/main/webapp/templates. You can change this around to match your source layout if this is too Maven-y for your tastes.
Note that JsTestDriver serves the assets with /test/ pre-pended to the URI specified in the serve attribute, so src/test/webapp/json/my_data.json is actually served from http:/ /localhost:9876/test/src/test/webapp/json/my_data.json.
server: http://localhost:9876
serve:
- src/main/webapp/templates/*.html
- src/test/webapp/json/*.json
load:
- src/main/webapp/js/*.js
- src/main/webapp/js/libs/*.js
test:
- src/test/webapp/js/*.js
Then, in a test case you can easily load a template and JSON data.
testLoadTemplateAndData : function () {
// Variables
var onComplete, template, data, expected, actual, templateLoaded, dataLoaded;
dataLoaded = false;
templateLoaded = false;
expected = "<h2>Your expected markup</h2>";
// Completion action
onComplete = function () {
if (dataLoaded && templateLoaded) {
// Render the template and data
actual = Mustache.to_html(template, data);
// Compare with expected
assertEquals('Markup should match', expected, actual);
}
};
// Load data with jQuery
$.ajax({
url : '/test/src/test/webapp/json/demo.json',
success :
function (result) {
data = result;
dataLoaded = true;
},
error :
function () {
fail("Data did not load.");
},
complete :
function () {
onComplete();
}
}
);
// Load the template with jQuery
$.get('/test/src/main/webapp/templates/demo.html',
function(result) {
template = result;
templateLoaded = true;
}
)
.error(function() { fail("Template did not load."); })
.complete(function() {
onComplete();
});
}
When both jQuery callbacks complete, Mustache should parse the template with the JSON data and render the expected output.
回答2:
I found a way to do it : is to mock ajax calls. On http://tddjs.com/ there is an example to do so in chapter 12, and the git repository is here : http://tddjs.com/code/12-abstracting-browser-differences-ajax.git
So the test code is :
TestCase("AppTest", {
setUp: function() {
tddjs.isLocal = stubFn(true);
var ajax = tddjs.ajax;
this.xhr = Object.create(fakeXMLHttpRequest);
this.xhr.send=function () {
this.readyState = 4;
this.onreadystatechange();
};
this.xhr.responseText="<expected data>";
ajax.create = stubFn(this.xhr);
},
"test article html ok": function () {
var actualHtml = "";
getHtml({ "title": "title", "header": "header", "text": "text", "authors": [ {"firstname": "firstname", "lastname": "lastname"} ] }, function(html) {
actualHtml = html;
});
assertEquals("<expected html>", actualHtml);
}
});
And the production code :
function getHtml(model, resultFunc) {
tddjs.ajax.get("http://url/to/template", {
success: function (xhr) {
resultFunc(mergeArticleModelAndView(model, xhr.responseText));
}
});
}
After having read jQuery code, I think it is possible to mock jQuery ajax request. Cf also jquery 1.5 mock ajax
回答3:
Yes it is possible to do quite the same with jquery :
Test code (just the setup the test is the same) :
TestCase("AppTest", {
setUp: function() {
this.xhr = Object.create(fakeXMLHttpRequest);
this.xhr.send=function () {
this.readyState = 4;
};
this.xhr.getAllResponseHeaders=stubFn({});
this.xhr.responseText="<expected data>";
jQuery.ajaxSettings.isLocal=stubFn(true);
jQuery.ajaxSettings.xhr=stubFn(this.xhr);
},
// same test method
And the production code :
function getHtml(model, resultFunc) {
$.get("/url/to/template", function(view) {
resultFunc(mergeArticleModelAndView(model, view));
});
}
That's the solution that we have chosen.
回答4:
With sinon.js it is even simpler :
TestCase("AppTest", {
setUp: function() {
this.server = sinon.fakeServer.create();
},
tearDown: function() {
this.server.restore();
},
"test article html ok": function () {
this.server.respondWith("mustache template");
this.server.respond();
var actualHtml = "";
getHtml({ "title": "title", "header": "header", "text": "text", "authors": [ {"firstname": "firstname", "lastname": "lastname"} ] }, function(html) {
actualHtml = html;
});
assertEquals("<expected html>", actualHtml);
}
来源:https://stackoverflow.com/questions/6717763/how-to-test-jquery-and-ajax-calls-using-jstestdriver