I'm testing some javascript libraries I've built by using ruby's harmony. Everything is working perfectly except AJAX calls — does anyone have any ideas how to achieve this?
My code looks something like this (I'm using RightJS):
my.js
function makerequest(argument) {
new Xhr('http://mysite.com/some/jsonp'),{
jsonp: true,
params: {something: argument},
onSuccess: function() {
// Calls a function defined outside my library
parse_response(this.json)
}
}).send()
}
test_makerequest.rb
require 'rubygems'
require 'harmony'
require 'test/unit'
require 'shoulda'
class RequestTest < Test::Unit::TestCase
context "The requesty thing" do
setup do
@page = Harmony::Page.new
@page.load(File.expand_path('js/my.js'))
end
should "get stuff from mysite.com" do
# Here I need to define a 'parse_response' which this
# should section will wait for and then do an 'assert'
# on the results.
results = callback_to_get_results_from__make_request
@page.execute('make_request("an argument")')
assert results == {'foo' => 'bar'}
end
end
end
So yeah, my question is, how should I assign results
above such that I can get the results of the async callback?
Async calls are generally problematic with JS unit testing. I've solved it in the past by making the XHR calls effectively synchronous by blocking the main thread until the XHR call succeeds. How to do this varies depending on your framework, and I'm unfamiliar with Harmony and RightJS. However, the logic would look something like this:
true
, when no XHR is in process the lock will befalse
.The intent of this approach is to avoid modifying your code, which would invalidate your tests. Unfortunately, this also means you can't test the return value itself, but I think this will be a caveat of any strategy that doesn't modify your code.
Another approach would be to actually run your tests asynchronously--ie. attach your assertion to the onSuccess callback using AOP. However, many testing suites don't play nicely with asynchronous tests, since the setup of one test may be starting before the teardown of the previous test (ie. because the previous test is still waiting for the async call to return).
One last approach would be to mock all asynchronous calls. Ie. just override XHR and have your test assert that it was called with the right arguments. I might favor this approach if I have integration tests that do go through the whole async stack.