How to use ngMock to inject $controller

那年仲夏 提交于 2020-01-06 15:07:16

问题


I'm trying to learn unit testing for Angular using Karma, Jasmine, and ngMock. There are at least 2 places in the Angular docs that show how to write unit tests for a controller, and I just have a couple of questions about how they're doing things.

From the Controller docs, section on testing controllers:

describe('myController function', function() {

  describe('myController', function() {
    var $scope;

    beforeEach(module('myApp'));

    beforeEach(inject(function($rootScope, $controller) {
      $scope = $rootScope.$new();
      $controller('MyController', {$scope: $scope});
    }));

    it("...");
  });
});

Question 1: This one mostly makes sense to me, but here's something I don't quite get. I understand that $controller is grabbing an instance of "MyController", but it looks like what is returned isn't being saved or used anywhere, so the only thing I can think of is that we grab that controller in order to get the correct $scope object? Even that seems like it isn't being saved in a variable anywhere, so I'm still a little confused on how this works behind the scenes. I had the same confusion about how module() works because we seem to be declaring which module we're using, but not saving or using it anywhere else. Is there something behind the scenes that caches the module/controller/scope so we don't have to save it manually or something?


Here's another example from the Unit Testing docs:

describe('PasswordController', function() {
  beforeEach(module('app'));

  var $controller;

  beforeEach(inject(function(_$controller_){
    // The injector unwraps the underscores (_) from around the parameter names when matching
    $controller = _$controller_;
  }));

  describe('$scope.grade', function() {
    it('sets the strength to "strong" if the password length is >8 chars', function() {
      var $scope = {};
      var controller = $controller('PasswordController', { $scope: $scope });
      $scope.password = 'longerthaneightchars';
      $scope.grade();
      expect($scope.strength).toEqual('strong');
    });
  });
});

Question 2: Here in the beforeEach function it's doing the underscore wrapping thing. Is that just so that you can keep the same service name throughout without having to change it?

Question 3: The it() block then uses $controller to do its thing, this time saving what gets returned to var controller but seemingly still never using it beyond this point. So why save it to a variable? The only thing I can think of offhand is that you save it in case you need to use it again inside this it() block, but they just didn't in this example?


I've been looking all over for a good explanation and I can't seem to find one. Sorry if there's a silly explanation I'm missing, but I'm on a time crunch and can't spend any more time spinning my wheels.


回答1:


1) Calling module(‘myApp’) will load your module, essentially running the declarations for all your mode functions (providers, directive, controllers) making them available for you to use later. thats how angular finds my Controller when calling $controller(‘myController’).

As for a reference to the controller returned from $controller, it depends on your implementation. If you are using $scope, Once the controller is instantiated you can reference the functions via $scope.

in the controller…

$scope.cancel = function () {
   doStuff();
};

Then your test can look like this...

describe(’test Controller', function () {
  var scope;

  beforeEach(module(‘myApp'));

  beforeEach(inject(function ($rootScope, $controller) {
    scope = $rootScope.$new();
    $controller(‘myController', {
      $scope: scope
    });
  }));

  describe(’testing stuff', function () {
    it(’test cancel function', function () {
      scope.cancel();
      expect(...);
    });
  });
});

If you are using controllerAs syntax and assigning functions to ‘this’ in the controller you can use a reference to the controller in your tests…

in the controller…

this.cancel = function () {
   doStuff();
};

Then your test can look like this...

describe(’test Controller', function () {
  var scope,
      controller;

  beforeEach(module(‘myApp'));

  beforeEach(inject(function ($rootScope, $controller) {
    scope = $rootScope.$new();
    controller = $controller(‘myController', {
      $scope: scope
    });
  }));

  describe(’testing stuff', function () {
    it(’test cancel function', function () {
      controller.cancel();
      expect(...);
    });
  });
});

2) Yes, the $controller is a convenience to avoid name collisions;

3) I think thats a bad example, if they are not using it they shouldn’t need to save it. I prefer to do all my test setup in the beforeEach ().




回答2:


@bobbyz : Second argument for $controller service would be hash of injectables. When you add reference for $scope, the same reference object will be modified when instance of controller is created as per definition of Controller. In that way, $scope is able to get all methods or variables.



来源:https://stackoverflow.com/questions/34549528/how-to-use-ngmock-to-inject-controller

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!