1

Here is the controller I'm testing:

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.');
      });
  };
}

And my tests:

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

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

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

  it('gets users', function() {
    var users = [ 'user1', 'user2', 'user3' ];
    $httpBackend
      .expectGET('/users')
      .respond(users);
    var adminController = createController();
    $httpBackend.flush();
    expect(adminController.users).toEqual(users);
  });

  it('has a method that deletes users', function() {
    $httpBackend.expectDELETE('/users/1').respond();
    $httpBackend.expectGET('/current-user').respond();
    $httpBackend.expectGET('/logout').respond();
    var adminController = createController();
    adminController.delete(1);
    $httpBackend.flush();
  });
});

It's currently failing:

enter image description here

It wants me to have an $httpBackend.expectGET('/users') in my second it block. I don't want to do that. To me, it seems more modular and organized if each it block tests a separate thing.


1) What should I do?

2) Why am I getting the $digest already in progress error?

Adam Zerner
  • 17,797
  • 15
  • 90
  • 156

1 Answers1

1
  1. You're right in that each it should test a separate thing. The it blocks are separate tests, they don't (shouldn't) depend on each other. The second test creates a new controller instance, which in turn tries to load users immediately. Even if you're not interested in this request here, you still need to set up the mock that handles it. You can also use the .when(...).respond(...) form instead of expect.

EDIT: One way to make this nicer would be to move the mocking of GET into a beforeEach as $httpBackend.expectGET('/users').respond(users). This way, the mock for the first GET would work for all it tests, so when testing something else, you wouldn't need to worry about it. As for the first it, maybe it would be enough to only do expect(adminController.users).toEqual(users). (There would be no explicit expectation about how the controller gets the users, only about the value -- but that obviously comes from the GET, so the coverage would be essentially the same.)

  1. Could it be something like this: https://github.com/angular/angular.js/pull/6522 ?
pdenes
  • 782
  • 6
  • 9
  • You say that the `it` blocks shouldn't depend on each other. It seems to me that having `$http.expectGET('/users')` in an `it` block is testing whether or not the request to `GET /users` is made. And so that `it` block now depends on `GET /users` when you might not want it to. – Adam Zerner Aug 08 '15 at 18:01
  • No, the two `it` blocks are independent: first, all the `beforeEach` functions will run, then the first `it`, then `afterAll`, then all this is repeated with the second `it`. So you'll have new state, with a new controller instance that will do the GET again. `$httpBackend` will also be a new instance, any expectations from a previous `it` are lost. The "wrong" solution would be to use a single `it` to test everything in sequence, but as you said, that's not how it should be done. The price is that you have to set up the environment each time. – pdenes Aug 08 '15 at 18:04
  • The only reason your `it` "depends" on the `GET /users` request is that this is what the controller (the "thing" under test) does, whenever you instantiate it. This means that any test for this controller will have to expect/deal with it. – pdenes Aug 08 '15 at 18:09
  • Ok, thanks for the explanation and the recommendation! – Adam Zerner Aug 08 '15 at 19:16