Testing AJAX with Test::Unit

If you want real end-to-end testing of a page with functioning AJAX, use Selenium. But I was interested in doing just a bit of JS speccing to make sure that the AJAX routes I called worked and that the data that came back fit the JS that I had written.

So, I figured with a little capybara and a little therubyracer, I could test my javascript with real route calls. Let's check it out.

The Javascript I want to test:

var dataObject = {};
var getRemoteData = function() {
  $.getJSON('/remote_path.json', function(data) {
    dataObject = $.parseJSON(data);
  });
}

We will pretend that hitting /remote_path.json returns the following response in JSON:

{ "my_information": "my response data" }

I want to test:

  1. That /remote_path.json is accessible and returns information
  2. That dataObject is populated with a Javascript Object containing the params I pass down

Here is my commented testing code:

test 'get some information via ajax' do
    # Create a new JS Context. :with => self means that
    # the global namespace is the current context
    js = V8::Context.new(:with => self)

    # Load up our JS file
    js.load(File.join(Rails.root, 'public', 'javascripts', 'application.js'))

    # Define a mock method for getting JSON data
    def getJSON(url, callback)
      # When the JS wants some data, get it for real w/ capybara
      visit url
      # And call the JS function passed in (ruby => js)
      callback.call(page.body)
    end

    js.eval %{
      /* Mock out jQuery */
      var $ = {};

      /* Setup the mock in the right place (JS => ruby) */
      $.getJSON = getJSON; 

      /* Mock out jquery parseJSON to call ruby JSON.parse
         Since the args are the same, it is plug and play!
         (JS => ruby) */
      $.parseJSON = JSON.parse;

      /* Trigger our JS */
      getRemoteData();

      /* Assert what we expect (JS => ruby) */
      assert_equal(" ", dataObject.access_key);
    }
  end

This is not an example of an incredibly useful test, but I found the ability to bind back and forth easily between ruby and javascript to be mind-blowingly incredible. I mean, look at that last line, where I call assert and pass a javascript object to it!

Huge thanks to Charles Lowell (cowboyd) for his hard work on therubyracer.

Enjoy the weekend!

Check out our other articles discussing Capybara:

How to Test PDF Content with Capybara