Skip to content

Instantly share code, notes, and snippets.

@aaronj1335
Last active December 14, 2015 19:38
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aaronj1335/5137633 to your computer and use it in GitHub Desktop.
Save aaronj1335/5137633 to your computer and use it in GitHub Desktop.
mocking up a resource definition

summary

each resource file, say api/enamel/1.0/infoset.js, defines an ancestor of the Model class. the resource class has a __requests__ member that defines mesh/request instances for each request (get, put, post, query, delete, etc). each of these request instances has an ajax method that's just a reference to $.ajax, and it uses that do actually make the xhr.

'mocking' a resource amounts to the following:

  1. defining a set of fixtures, a.k.a. an array of objects with fields corresponding to the given resource
  2. swapping out the 'ajax' method of each of the resource's requests to respond with data from the fixtures (no xhr is ever made)

keep in mind we're monkey-patching the resource. so say we've got a unit test file at src/foo/test.js, and it requires the infoset-mocking file auxl/test/mock/infoset.js. then every other module using infosets get the mocked version, even if they required the real resource at api/enamel/1.0/infoset.js. this is good because you don't have to modify the real code to create a test environment, and you can just define the resources you'd like to mock in the test file.

walk through

say we'd like to mock the 'matter' resource that lives at:

api/docket.document/1.0/siq.matter.js

we'll put the mocked resource in auxl:

auxl/test/mock/matter.js

step 1

start by defining fixtures in JSON at:

auxl/test/mock/matterfixtures.json

i usually get the fixture list by querying the api from the chrome dev tools (with like a Matter.collection().load()), and then copy/pasting the result from the 'network' tab. the result looks something like this:

[
  {
    "defunct": false,
    "name": "second ever mocked",
    "designation": null,
    "created": "2013-03-06T14:08:00Z",
    "modified": "2013-03-06T14:10:39Z",
    "id": "458f4360-8f20-4c00-9391-6a92897aca67",
    "description": null
  },
  {
    "defunct": false,
    "name": "third ever mocked",
    "designation": null,
    "created": "2013-03-06T22:11:09Z",
    "modified": "2013-03-06T22:11:09Z",
    "id": "bea8ba82-2d1c-4b18-ac2b-0a0f82d3dd53",
    "description": "homie we major"
  },
  // ... etc ...

alternatively, if the api is not available, or you don't have any defined, you may want to just write like a _.map() function to go through and spit out a list of these objects. some of the example fixtures are actually javascript that generates the data on the fly at page load. either way just paste the results into the *.json file.

step 2

next we'll define the mocked resource that intercepts ajax calls. define a module in auxl/test/mock/matter.js that looks like this:

define([
    'mesh/tests/mockutils',
    'api/docket.document/1.0/siq.matter',
    'text!./matterfixtures.json'
], function(mock, Matter, fixturesJson) {
    var fixtures = JSON.parse(fixturesJson);
    return mock('matter', Matter, fixtures);
});

breaking it down by lines:

  • line 2: the mesh/tests/mockutils module returns a function that does all of the boilerplate associated with swapping out those 'ajax' methods on the requests. more on that in a second.
  • line 3: the real resource, of course
  • line 4: our fixtures file we defined in step 1
  • line 7: this is where the magic happens
    • 1st arg: the mocked resource is attached to window at this name, since it's really convenient to be able to say window.matter in testing situations
    • 2nd arg: the resource that you want to be mocked
    • 3rd arg: the fixtures -- this should be an array
    • return value: the same resource, just for convenience since we're monkey patching, it's === to Matter

usage

so your test file will look something like this:

/*global test, asyncTest, ok, equal, deepEqual, start, module, strictEqual, notStrictEqual, raises*/
define([
    './../foo',
    'auxl/test/mock/matter',
    './../../styles'
], function(Foo) {
    asyncTest('instantiating a Foo', function() {
        var f = Foo().appendTo('body');
        ok(true);
        start();
    });
    start();
});

and assuming that Foo loads a list of matters, you'll be pulling from the fixture data.

note that mesh/tests/mockutils provides a lot, including:

  • a way to specify the delay before responses:
// out-of-order load
var collection = Resource.collection();
Resource.mockDelay(50); // in millisecods
var load1 = collection.load();
Resource.mockDelay(0);
var load2 = collection.load({refresh: true});
// load2 will resolve before load1
  • a way to mock failures:
var m1 = Resource.models.get(0);
m1.load();                          // works
Resource.mockFailure(true);
var m2 = Resource.models.get(1);
m2.load();                          // fails
Resource.mockFailure();
var m3 = Resource.models.get(2);
m3.load();                          // works
  • a more powerful means to intercept requests:
var reqs = [];
Resource.mockWrapRequestHandler('update', function(f, params) {
    var args = Array.prototype.slice.call(arguments, 1);
    reqs.push(JSON.parse(params.data));
    return f.apply(this, args);
});
var m = Resource.models.get(0);
m.set('foo', 'bar');
m.save();
// the params for the request are now available on the 'reqs' var
  • a means of changing the fixtures that the resource pulls from, so you can simulate a database update on the backend:
var myModel = Resource.models.get(0);
Resource.mockDataChange(function(exampleFixtures) {
    var cur = _.find(exampleFixtures, function(f) {
        return f.id === myModel.get('id');
    });
    cur.required_field = 'changed value';
});

however if you use any of the above functionality, you'll want to reset it to the original value at the beginning of the next test (this should definitely be a library function):

Resource
    .mockDelay()
    .mockFailure()
    .mockDataChange()
    .mockUnwrapRequestHandlers()
    .models.clear();

examples

@ralphsmith80
Copy link

The library method mockReset as been added to mesh/test/mockutils. This handles the above mentioned case for:

Resource
    .mockDelay()
    .mockFailure()
    .mockDataChange()
    .mockUnwrapRequestHandlers()
    .models.clear();

An example of it's use can be found in mesh/test/test_model_consistency.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment