Mocking HTTP Requests with Vue.js and Vue-Resource

While writing unit tests for a growing Vue.js application last week, I needed a simple way to return test data for various HTTP requests that vue-resource fired off, without actually querying the server. There are libraries out there that can help with mocks and stubs and spies and a thousand other situations, but my needs were simple. Simple needs often call for simple solutions.

Vue-resource gives us request interceptors that allow us to mutate every step of the request processing. It would be trivial to interrupt the request cycle and return phony data.

import Vue from 'vue';
import VueResource from 'vue-resource';

Vue.use(VueResource);

Vue.http.interceptors.unshift((request, next) => {
  next(
    request.respondWith(
      {id: 17, body: 'Well, my time of not taking you seriously is coming to a middle.'},
      {status: 200}
    )
  );

This adds a simple interceptor to the list. We stop processing the request by calling request.respondWith() - this ensures that a network call is not made, and prevents additional interceptors from running.

But this isn't good enough! Rarely do we make only 1 HTTP request. There are often multiple requests made to different API endpoints. Right now the code only ever returns a single response. I wanted to essentially map out my API route tree, along with the mock data we would return.

let routes = [
  {
    method: 'GET',
    url: 'quotes/',
    response: [
      {id: 14, body: "You know what the chain of command is? It's the chain I go get and beat you with until you understand who's in ruttin charge here."},
      {id: 22, body: 'Also? I can kill you with my brain.'}
    ]
  },
  {
    method: 'POST',
    url: 'quotes/',
    response: {id: 23, body: 'Terse? I can be terse. Once, in flight school, I was laconic.'}
  },
  {
    method: 'GET',
    url: 'quotes/18/',
    response: {id: 18, body: 'Curse your sudden but inevitable betrayal!'}
  }
];

Now we just need a simple way to determine which route matches the HTTP request being made, and return the appropriate response. Let's modify the interceptor.

import Vue from 'vue';
import VueResource from 'vue-resource';

Vue.use(VueResource);

Vue.http.interceptors.unshift((request, next) => {
  let route = routes.find((item) => {
    return (request.method === item.method && request.url === item.url);
  });
  if (!route) {
    // we're just going to return a 404 here, since we don't want our test suite making a real HTTP request
    next(request.respondWith({status: 404, statusText: 'Oh no! Not found!'}));
  } else {
    next(
      request.respondWith(
        route.response,
        {status: 200}
      )
    );
  }
});

Simple!

Here's a little cornball application on CodePen that shows off this interceptor. You could also use this same code during development if you're building the front-end for an API that doesn't yet exist. Instead of hard coding test data into your application, you could build out the request/response handling from the start.

Of course, it doesn't handle every scenario. We might need to deal with more complex POST data, or return different responses for the same API endpoint depending on additional information passed in the request. For simple purposes, I find having a small amount of easy to understand code is better than introducing additional complexity by bringing in yet another library.