AJAX testing with Jasmine 2.0 - 01
April 23, 2014
Diligently following along the lines traced by online guides and screen casts I've written my tests, when it came to mocking AJAX requests in a jasmine-jquery
test suite, like
it("should load new content when an internal link is clicked", function() {
// set up the spies
spyOn($, 'ajax').and.returnValue('<p>some new lorem</p>');
spyOn($, 'isUrlExternal').and.returnValue(false);
// ajaxify is the jQuery plugin I'm developing
$frag.ajaxify().find('a[href="/some/url"]').trigger('click');
// check that the response content has been used
expect($frag.find("#content").text()).toBe('some new lorem');
});
but that will not work at all. Jasmine 2.0
is out and things will be done differently, specifically using the jasmine-ajax
plugin.
Adding the plugin to the Gruntfile
That's an easy one and the position of the jasmine-ajax
plugin in the stack will make no difference since the jasmine
source file is loaded, in a grunt
managed context, before any other source file.
jasmine: {
src: 'assets/js/src/*.js',
options: {
vendor: [
'assets/js/vendor/jQuery/jquery.js',
'assets/js/vendor/jasmine/jasmine-jquery.js',
'assets/js/vendor/jasmine/jasmine-ajax.js',
'assets/js/vendor/jQuery/jquery.urlInternal.min.js'
],
specs: 'assets/js/spec/*.js',
keepRunner: true
}
}
Rewriting tests
So, following along the lines of the jasmine-ajax
tutorial the way to make AJAX-based tests work might be
it("should load new content when an internal link is clicked", function() {
// mock the check
spyOn($, 'isUrlExternal').and.returnValue(false);
// make the ajax request, will use the 'load' method internally
$frag.ajaxify().find('a[href="/some/url"]').trigger('click');
// mock the ajax request response
jasmine.Ajax.requests.mostRecent().response({
"status": 200,
"content/type": "text/html",
"responseText": "<p>some new lorem</p>"
});
// BAD testing code, BAD, temporary solution
// check the new content after some time has passed
setTimeout(function(){
expect($frag.find("#content").text()).toBe('some new lorem');
}, 5000);
});
But this code smells very bad. It actually works but it's not good at all for a long list of reasons.
I'll dig deeper into the question to make my code smell less and will probably allow for callback injection in the plugin.
Bonus: keeping the spec runner HTML file
In many online tutorials an HTML spec runner file is used in place of grunt
ugly and utilitarian CLI; by default grunt-contrib-jasmine
will delete the spec runner after each test run but setting keepRunner
to true
will avoid that.
Opening the HTML spec runner file in a browser will raise an error for each spec if any kind of fixture loading is involved: simply put fixtures are loaded using AJAX methods and file access is not allowed from HTML files by default.
In Chrome the problem is quickly solved opening Chrome from the command line, Mac here, like
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --allow-file-access-from-files &
the hint comes from StackOverflow and I will take no credit for it.