简体   繁体   中英

Asynchronous Unit Tests in Ruby

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:

  1. Override your XHR method so that it maintains a lock/mutex. The lock can be a simple boolean. When any XHR is in process, the lock will be true , when no XHR is in process the lock will be false .
  2. In your test, before you run your assertion, add a loop that executes your XHR and then waits until the lock clears.
  3. Once the loop finishes, you'll be sure the XHR has completed, and you can run your assertion.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM