Mocking $.ajax request functions with Sycamore


Revisiting Sycamore

A while back I wrote about a JavaScript mixin that I wrote called Sycamore for managing and organizing jQuery $.ajax requests. I've had great success with this library on multiple client projects now. Recently I've taken advantage of a feature of it that I've found especially useful on multiple occasions, so I figured I should write something about it.

In the blog post I previously wrote about it, and in the documentation for it, I've always written about using the execute() function that Sycamore exposes. However, I have a branch of the project (this feature isn't as complete as I'd like it to be, which is why it hasn't yet been merged into the master branch) that includes an additional module that I call the DataClient, which is a nice little enhancement to the project (perhaps writing this post will inspire me to finish up that feature of the project...if you're interested in it, nag me about it and maybe that'll motivate me too). Here's the main thing that it brings to the table:

The execute() function is pretty simple, and it takes a request definition object, transforms it, and executes a jQuery.ajax() call and returns the $.Deferred object returned by the ajax call, which lets you chain .done() and .fail() handlers, etc. It works fine.

The DataClient factory

The DataClient module is a factory function that takes a request configuration object, transforms it, and returns an object that has functions that can be executed that represent each of the requests defined by the configuration passed to it. This may be clearer with a simple example.

Given the following request definition object/configuration:

var requestConfiguration = {  
  requests: {
    getSomeData: {
      url: "/some/data"
    },

    getSomeOtherData: {
      url: "/some/other/data"
    },

    saveSomeStuff: {
      url: "/some/stuff",
      type: "post"
    }
  }
};

When it is passed to the DataClient factory:

var dataClient = new DataClient(requestConfiguration);  

The returned dataClient is an object that has functions that correspond to the names of the requests defined in requestConfiguration, each of which can be very neatly and understandably be executed like:

// assume `getSomeData` doesn't require any parameters
dataClient.getSomeData().done(...).fail(...);

// assume `getSomeOtherData` requires an `id` parameter
dataClient.getSomeOtherData({ id: 1234 }).done(...).fail(...);

// `saveSomeStuff` will save an array of objects to the database
dataClient.saveSomeStuff([{ id: 1, name: "foo" }, { id: 2, name: "bar" }]).done(...).fail(...);  

Hopefully that makes it pretty clear how nice and useful the the DataClient module can be. I love this pattern, and have been using it in all of my client projects where I need and use jQuery's $.ajax functionality.

Mocking ajax requests

Here is where it gets especially useful. Recently I was working on a UI feature that required an ajax call, but the server endpoint that I needed to call wasn't ready or available yet. I had discussed the feature with the backend developer that was working on it, and we had agreed on the contract for the endpoint, but it just wasn't there yet. So, to work around the unavailability of the server endpoint, and continue working on the UI, I was able to just override the dataClient (note the lowercase "d" in dataClient, I'm referring to the instance, not the factory function itself) function that was making this ajax call to simulate it. Overriding that function was really simple.

Let's say the dataClient function that I wanted to mock/override is called saveUserPreferences. I can mock/override this function like this:

dataClient.saveUserPreferences = function (data) {  
  var simulatedLatency = _.random(250, 3000),
      proxy = $.Deferred(),
      response = {
        status: 200,
        message: "User preferences saved successfully",
        data: "Add any other data you want to this simulated response object"
      };

  setTimeout(function () {
    proxy.resolve(response);
    // or, if you'd rather simulate a failure...
    // proxy.reject({
    //   status: 500,
    //   message: "Something went wrong"
    // });
  }, simulatedLatency);

  return proxy.promise();
};

OK, let's talk about what's going on here. In the actual dataClient instance, the saveUserPreferences function will accept a data payload, and use it under-the-covers to execute a jQuery ajax call, and then return the $.Deferred object that those calls always return, so that you can chain done, fail, when or any other promise handlers you need to chain to it. The jQuery library exposes its $.Deferred as an object that can be used anywhere, so we can use it in our overridden function to simulate our dataClient.saveUserPreferences function returning the same thing that an actual ajax call would after a period of simulated latency for the call. The call to resolve takes an object that you wish to simulate the data that would be returned from the server, or if/when you wish, the same thing with the call to reject.

This ability to simulate ajax calls to the server during development when backend endpoints aren't immediately available has been an invaluable tool in my workflow. Maybe it can be for you too. Hope this is helpful.


CREDITS: I can't really take credit for the DataClient factory. I collaborated on its development, but it was largely inspired by and the brainchild of Ryan Niemeyer of KnockMeOut, and it likely wouldn't exist today without his help. Thanks a ton Ryan.