DRY Up Your $httpBackend mock

angularjs-logo

In the previous post, I covered how to seamlessly mock $httpBackend for unit & E2E testings at the same time. With the same structure and some additional settings, described below, I discovered additional benefits to this approach. It is possible to make your tests DRY while simultaneously giving you the power to manipulate the mock data in E2E testing. In this post, I will discuss how to make $httpBackend mock DRY. (If you haven’t already read the previous post, you may want to go back and read it now for the necessary context.)

In AngularJS $httpBackend is implemented differently for unit testing and E2E testing. However, it is designed so that both have the same API for configuring how the HTTP request should be mocked, using the when methods (whenDELETE, whenHEAD, whenGET, whenPOST, whenPUT). You specify the URL and optionally what data and/or headers to send.

$httpBackend.whenPOST(/sessions\/new/, {UserId: 'payjoe', Password: 'password'})
  .respond({
      authToken: 'xxxxxxxxxx',
      userData: {
        UserId: 'payjoe'
        // Other data
      }
  });

So it would DRY your test code if we could set these up in one place for both types of tests. Here’s how:

Create a factory in the mocks folder inside app/scripts/, say userLogin.js. Personally, I would create one file per URL which we want to mock. The reason for this is because the file will contain both the data and response mocks.

'use strict';

angular.module('mocks.userLogin', [])
  .value('signInMocksSet', {
    urlRegex: /msg\/sessions\/new\/json/,
    successful : {
      UserId    : 'realUser',
      Password  : 'password'
    },
    incorrect : {
      UserId    : 'fakeUser',
      Password  : 'password'
    }
  })
  .factory('signInSuccessful', function() {
    return {
      // put response from successful login
    };
  })
  .factory('signInError', function() {
    return {
      // put response from failed login
    };
  })

For unit testing, we can then inject the factory in a beforeEach block and set the $httpBackend mocks.

describe('Controller: LoginCtrl', function() {

  beforeEach(module('SuperApp', 'SuperApp.mocks'));

  var mockValues = {};

  beforeEach(inject(function($injector) {
    injector    = $injector;

    // setup the controller

    mockValues.signInMocksSet   = injector.get('signInMocksSet');
    mockValues.signInSuccessful = injector.get('signInSuccessful');
    mockValues.signInError      = injector.get('signInError');

    $httpBackend.whenPOST(mockValues.signInMocksSet.urlRegex,
                          mockValues.signInMocksSet.successful)
      .respond(mockValues.signInSuccessful);

    $httpBackend.whenPOST(mockValues.signInMocksSet.urlRegex,
                          mockValues.signInMocksSet.incorrect)
      .respond(mockValues.signInError);
  }));

  // your specs
});

Similarly, for E2E testing, the factory is injected in e2e-mocks.js and set the $httpBackend mocks there.

'use strict';

angular.module('e2e-mocks', ['ngMockE2E', 'mocks.userLogin'])
  .run(
    function($httpBackend, signInMocksSet, signInSuccessful, signInError) {
      $httpBackend.whenPOST(signInMocksSet.urlRegex,
                            signInMocksSet.successful)
        .respond(signInSuccessful);

      $httpBackend.whenPOST(signInMocksSet.urlRegex,
                            signInMocksSet.incorrect)
        .respond(signInError);
    }
  )

Don’t forget to put the factory file to the main index.html

<!-- build:js -->
<script src="components/angular-mocks/angular-mocks.js"></script>
<script src="scripts/mocks/userLogin.js"></script>
<!-- endbuild -->

Hope it helps :)

One Response to “DRY Up Your $httpBackend mock”

  1. Pingback: AngularJS Highlights: Week Ending 3 November 2013 | SyntaxSpectrum