Prototype Angular UIs Without A Backend

Doug Turnbull — September 16, 2013 | 7 Comments | Filed in: javascript, Rapid Prototyping
vectors are fun

So you’ve got an AngularJS UI built out, but you’ll need a fleshed-out backend before being able to really take it for a test drive, right? Actually, it turns out, with the magic of Angular and its mocked $httpBackend, we don’t need no stinking backend!

If you’ve heard of $httpBackend, you’ve probably heard of it it terms of writing unit tests. Tests that look like:

    // tell http backend what to do when in gets a GET request at a specific URL
    $httpBackend.when(“GET”, “/users/4”).respond( {userName: “Doug”, userId: 4} );
    // Code we’re testing
    userService.getUserById(4);
    // flush all pending requests (pretend the server just got back with response)
    $httpBackend.flush();
    expect(userService.getUser(4)).toBe( {userName: “Doug”, userId: 4});

While unit testing like this can help test and play with bits of code, it can’t drive an overall vision the way that seeing, clicking, and typing into a prototype can. Therefore, I’ve taken $httpBackend and implemented a complete mock-up of my backend to allow me to use my UI and iterate quickly. Basically, I can now simply open file://path/to/my/project/index.html in my browser use my application as if it were backed by a database on a server. (That is as long as I don’t fully reload the page:) ).

How have I done this? Read along with the corresponding jsfiddle

First, I tell Angular to use the angular-mocks $httpBackend (by passing it the $httpBackend constructor function) as a decorator on top of the concrete $httpBackend service. In Angular’s dependency injection, a decorator wraps the original service, layering on some custom functionality. In this case, Angular’s mock $httpBackend is setup to receive the concrete $httpBackend service, and will pass through to it if you use the passThrough() function after creating a rule (examples further down). In other words, I reserve the ability to pass some requests along as real HTTP requests.

   myApp
   .config(function($provide) { 
      $provide.decorator('$httpBackend', createHttpBackendMock); 
   });

All our code to mock the backend will take place in this function we execute before running the angular application:

    myApp.run(function($httpBackend, $timeout) {
            … <the cool stuff you’ll see below>
        });

In this run function, I can use the mock $httpBackend API to specify some rules, with actions to perform on the receipt of HTTP requests:

    // Some state
    var users = {};
    var userId = 0;
    $httpBackend.when('PUT', '/users')
    .respond(function(method, url, data) {
      data = angular.fromJson(data);
      users[userId] = {userName: data.userName, userId: userId};
      userId++;
      return [200, users[data.userId]];
    });

I can then specify some rules based on a regex if need be:

    var regexpUrl = function(regexp) {
      return {
        test: function(url) {
          this.matches = url.match(regexp);
          return this.matches && this.matches.length > 0;
        }
      };
    };

    $httpBackend.when('GET', regexpUrl(/users\/(\d+)/))
    .respond(function(method, url, data) {
      data = angular.fromJson(data);
      return [200, users[data.userId]];
    });

And since I’m using a delegate, I can decide that certain requests will just get passed through to do real requests (say JSONP requests to solr from my instant search directive!):

    $httpBackend.when('JSONP', regexpUrl(/http:\/\/.*/))
      .passThrough();

$httpBackend’s way of simulating an asynchronous response is for you to call flush() from your tests. So none of our app’s requests will be responded to until we tell $httpBackend to flush. To create a kind-of $flush run-loop, we’ll use the angular $timeout service to call flush every half-second. $httpBackend still wants to be used in a unit testing context, so it will throw an exception if there’s nothing to flush. So we catch and discard that exception.

    var flushBackend = function() {
      try {
        $httpBackend.flush();
      } catch (err) {
      // ignore that there's nothing to flush
      }
      $timeout(flushBackend, 500 /*ms*/);
   };
   $timeout(flushBackend, 500);

Viola, now I can experiment with my UI much more efficiently! Compared to other solutions, like mocking individual angular services or creating a mockable backend service that wraps the $http calls, I have found this solution the cleanest and least imposing on my production code.

Have you had to solve a similar problem? Please comment and let me know! Also, do you need help with rich search & discovery oriented user interfaces? Let us know. We know quite a bit about building rich UIs for Solr!

7 comments on “Prototype Angular UIs Without A Backend

  1. Hi there. It’s not clear to me why you define a custom function that tests a regexp if that can be done without defining a custom function, like so:

    $httpBackend.whenPOST(/\/events\/\d+\/arrangements\/\d+\/items$/)

    Can you please clarify what is the advantage of your method?

  2. @kenild I am aware of that feature, but for some reason I just could not get it to work. If you want to fork my jsfiddle and send it back to me, please do so I can see what I was doing wrong.

  3. Nice stuff, this is just what I need when I’m debugging IE8-9 since they don’t support CORS with XMLHttpRequest.

    I have a question though, how would I respond with a local static .json? When I try return [200, '/path/to/data.json']; it actually returns a text string.

  4. Hey, this is an awesome tip, thanks for the suggestion.

    With regard to ‘flushing’, in Angular 1.2.x, $http requests aren’t actually sent until the end of a $digest cycle, so it might make your mocks even more accurate/helpful to bind to $rootScope.$watch('', flushFn)

Comments are closed.

Developed in Charlottesville, VA | ©2013 – OpenSource Connections, LLC