Search code examples
angularjskarma-runnerhttpbackend

Where should $httpBackend.expect() go?


The following test passes:

admin.controller.js

angular
  .module('mean-starter')
  .controller('AdminController', AdminController);

function AdminController(User, Auth, $state) {
  var vm = this;
  User
    .list()
    .success(function(data) {
      vm.users = data;
    })
    .error(function() {
      console.log('Problem getting users.');
    });

  vm.delete = function(id) {
    User
      .delete(id)
      .success(function(data) {
        if (Auth.getCurrentUser()._id === id) Auth.logout(); // deleting yourself
        else $state.reload();
      })
      .error(function() {
        console.log('Problem deleting user.');
      });
  };
}

admin.controller.spec.js

describe('AdminController', function() {
  var AdminController, $httpBackend;

  beforeEach(module('mean-starter'));
  beforeEach(module('templates'));
  beforeEach(inject(function($controller, $rootScope, _$httpBackend_) {
    $httpBackend = _$httpBackend_;
    AdminController = $controller('AdminController');
  }));

  afterEach(function() {
    $httpBackend.verifyNoOutstandingExpectation();
    $httpBackend.verifyNoOutstandingRequest();
  });

  it('gets users', function() {
    $httpBackend
      .expectGET('/users')
      .respond('foo');
    $httpBackend.flush();
  });
});

I wouldn't expect it to. Here is what I expected to happen:

  1. The controller is instantiated in the beforeEach.
  2. User.list() gets run.
  3. The $http isn't yet overridden by $httpBackend, so the request goes out normally.
  4. $httpBackend.expectGET('/users').respond('foo') expects GET /users. And says, "I'll respond with 'foo' if I get that request".
  5. $httpBackend.flush() says "Send out the defined responses for any of the requests that $httpBackend received."
  6. .expectGET fails because it doesn't receive it's request (the request happened before the expectation).
  7. .flush() throws an error because there's nothing to flush.

I'm not getting the outcomes I was expecting, so something about my logic above must be wrong - what is it?


Solution

  • The $http isn't yet overridden by $httpBackend, so the request goes out normally.

    This is not a correct assumption. $httpBackend is used automatically in unit tests (it's part of the ng-mocks module that's used in unit tests). So whether you use $httpBackend or not in your unit test code, it's there and it's processing all the $http requests that your code makes.

    Think about it, if it wasn't doing this, your unit tests could make real requests.

    EDIT

    For cases like this, where the controller is making an HTTP request as soon as it's instantiated, I like to put the $httpBackend.expectGET() call in the beforeEach block right before you instantiate the controller.

    I would also flush() the backend in the beforeEach block as well. This, I think, makes it clear that these requests happen at controller startup. And it means I don't have to make this expectation in every unit test.