• function

can.fixture can.fixture

 

can.fixture(fromUrl, toUrl)

Trap requests from one url and redirect them from another.

Parameters

  1. fromUrl {String}

    Trap requests made by can.ajax to this url.

  2. toUrl {String}

    Redirect requests to this url.

can.fixture(url, handler(request, response))

Trap requests to a url and provide the response with a callback function.

Parameters

  1. url {String}

    Trap requests made by can.ajax to this url.

    The url can be templated with tags that look like {TEMPLATE}. For example: "/users/{id}". Any templated values get added to the handler's request object's data.

  2. handler {requestHandler(request, response)}

    Specifies the response of the fixture. handler gets called with the can.ajax settings object and a response handler that is used to specify the response.

can.fixture(fixtures)

Configures multiple ajax traps.

Parameters

  1. fixtures {Object<url,requestHandler(request, response) | String>}

    An mapping of templated urls to redirect urls or request handler functions.

    can.fixture({
      "/tasks": "/fixtures/tasks.json",
      "DESTROY /tasks/{id}": function(){
          return {};
      }
    })
    

Simulate AJAX requests.

can.fixture intercepts an AJAX request and simulates the response with a file or function. They are a great technique when you want to develop JavaScript independently of the backend.

Types of Fixtures

There are two common ways of using fixtures. The first is to map Ajax requests to another file. The following intercepts requests to /tasks.json and directs them to fixtures/tasks.json:

can.fixture("/tasks.json", "fixtures/tasks.json");

The other common option is to generate the Ajax response with a function. The following intercepts updating tasks at /tasks/ID.json and responds with updated data:

can.fixture("GET /tasks/{id}",function(request,response){
  response(200,"success",{id: request.data.id, name: "fix tires."})
})

We categorize fixtures into the following types:

  • Static - the response is in a file.
  • Dynamic - the response is generated by a function.

There are different ways to lookup static and dynamic fixtures.

Static Fixtures

Static fixtures use an alternate url as the response of the Ajax request.

// looks in fixtures/tasks1.json relative to page
can.fixture("tasks", "fixtures/tasks.json");

// looks absolute to the page
can.fixture("tasks", "//fixtures/tasks.json");

Static fixtures can also be templated, which means that parameters will be used in the fixture filename:

// looks in fixtures/tasks1.json relative to page
can.fixture("tasks/{id}", "fixtures/tasks.{id}.json");

A request to tasks/42 will look for a fixtures/tasks.42.json file.

Dynamic Fixtures

Dynamic Fixtures are functions that get the details of the Ajax request and return the result of the mocked service request from your server.

For example, the following returns a successful response with JSON data from the server:

can.fixture("/foobar.json",
  function(original, response){
    response(200, "success", { json: {foo: "bar" } }, {})
  })

The fixture function has the following signature:

function( originalOptions, response) {
  response(status, statusText, responses, responseHeaders);
}

where the fixture function is called with:

  • originalOptions - are the options provided to the ajax method, unmodified, and thus, without defaults from ajaxSettings
  • response - the response callback. It can be called with:
    • status - the HTTP status code of the response.
    • statusText - the status text of the response
    • responses - a map of dataType/value that contains the responses for each data format supported
    • responseHeaders - response headers
  • options - are the request options
  • headers - a map of key/value request headers

However, can.fixture handles the common case where you want a successful response with JSON data. The previous can be written like:

can.fixture("/foobar.json",
  function(original, response){
    response({ foo: "bar" });
  })

Since response is called asynchronously you can also set a custom fixture timeout like this:

can.fixture("/foobar.json",
function(original, response){
    setTimeout(function() {
      response({ foo: "bar" });
    }, 1000);
  })

If you want to return an array of data respond like this:

can.fixture("/tasks.json",
  function(original, response){
    response([ "first", "second", "third"]);
  })

Note: A fixture function can also return its response directly like this:

can.fixture("/foobar.json", function() {
  return { foo: "bar" };
})

This is kept for backwards compatibility and should not be used.

can.fixture works closesly with jQuery's ajaxTransport system.

Templated Urls

Often, you want a dynamic fixture to handle urls for multiple resources (for example a REST url scheme). can.fixture's templated urls allow you to match urls with a wildcard.

The following example simulates services that get and update 100 todos.

// create todos
var todos = {};
for(var i = 0; i < 100; i++) {
  todos[i] = {
    id: i,
    name: "Todo "+i
  }
}
can.fixture("GET /todos/{id}",
  function(original, response, settings){
    // return the JSON data
    // notice that id is pulled from the url and added to data
    response(todos[orig.data.id]);
  })

can.fixture("PUT /todos/{id}",
  function(original, response, settings){
    // update the todo's data
    can.extend(todos[orig.data.id], orig.data );
    response({});
  })

Notice that data found in templated urls (ex: {id}) is added to the original data object.

Simulating Errors

The following simulates an unauthorized request to /foo.

can.fixture("/foo",
  function(original, response) {
    response(401,"{type: 'unauthorized'}");
  });

This could be received by the following Ajax request:

can.ajax({
  url: '/foo',
  error : function(jqXhr, status, statusText){
    // status === 'error'
    // statusText === "{type: 'unauthorized'}"
  }
})

Turning off Fixtures

You can remove a fixture by passing null for the fixture option:

// add a fixture
can.fixture("GET todos.json","//fixtures/todos.json");

// remove the fixture
can.fixture("GET todos.json", null)

You can also set can.fixture.on to false:

can.fixture.on = false;

can.fixture.store

can.fixture.store makes a CRUD service layer that handles sorting, grouping, filtering and more. Use it with a can.Model like this:

var Todo = can.Model({
  findAll : 'GET /todos',
  findOne : 'GET /todos/{id}',
  create  : 'POST /todos',
  update  : 'PUT /todos/{id}',
  destroy : 'DELETE /todos/{id}'
  }, {});

var store = can.fixture.store(100, function(i) {
  return {
    id : i,
    name : 'Todo ' + i
  }
});

can.fixture('GET /todos', store.findAll);
can.fixture('GET /todos/{id}', store.findOne);
can.fixture('POST /todos', store.create);
can.fixture('PUT /todos/{id}', store.update);
can.fixture('DELETE /todos/{id}', store.destroy);

Testing Performance

Dynamic fixtures are awesome for performance testing. Want to see what 10000 files does to your app's performance? Make a fixture that returns 10000 items.

What to see what the app feels like when a request takes 5 seconds to return? Set can.fixture.delay to 5000.

Organizing fixtures

The best way of organizing fixtures is to have a 'fixtures.js' file that steals can/util/fixture and defines all your fixtures. For example, if you have a 'todo' application, you might have todo/fixtures/fixtures.js look like:

steal({
        path: '//can/util/fixture.js',
        ignore: true
      })
      .then(function(){

  can.fixture({
      type: 'get',
      url: '/services/todos.json'
    },
    '//todo/fixtures/todos.json');

  can.fixture({
      type: 'post',
      url: '/services/todos.json'
    },
    function(original, response, settings){
        response({
            id: Math.random(),
            name: settings.data.name
        })
    });

})

Notice: We used steal's ignore option to prevent loading the fixture plugin in production.

Finally, we steal todo/fixtures/fixtures.js in the app file (todo/todo.js) like:

steal({path: '//todo/fixtures/fixtures.js',ignore: true});

//start of your app's steals
steal( ... )

We typically keep it a one liner so it's easy to comment out.

Switching Between Sets of Fixtures

If you are using fixtures for testing, you often want to use different sets of fixtures. You can add something like the following to your fixtures.js file:

if( /fixtureSet1/.test( window.location.search) ){
  can.fixture("/foo","//foo/fixtures/foo1.json');
} else if(/fixtureSet2/.test( window.location.search)){
  can.fixture("/foo","//foo/fixtures/foo1.json');
} else {
  // default fixtures (maybe no fixtures)
}