Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
220 views
in Technique[技术] by (71.8m points)

javascript - Testing $scope in AngularJs controller with dependency on $filter

I've gone through a few tutorials and basic examples but I'm having a hard time writing unit tests for my controller. I've seen code snippets instantiating controllers and letting angular inject the $rootScope object which in turn is used to create a new scope object for the controller. But I can't figure out why ctrl.$scope? is undefined:

 describe('EmployeeCtrl', function () {
    var scope, ctrl, $httpBackend;

    beforeEach(inject(function (_$httpBackend_, $rootScope, $controller, $filter) {
        $httpBackend = _$httpBackend_;       

        scope = $rootScope.$new();
        ctrl = $controller('EmployeeCtrl', { $scope: scope});
        expect(ctrl).not.toBeUndefined();
        expect(scope).not.toBeUndefined();   //<-- PASS!      
        expect(ctrl.$scope).not.toBeUndefined();  //<-- FAIL!       
    }));
});

I ended up using the scope variable instead of ctrl.$scope but then on my first test I couldn't figure out how to unit test a function variable inside my controller:

Controller:

 function EmployeeCtrl($scope, $http, $filter, Employee) {
  var searchMatch = function (haystack, needle) {
   return false;
  }
 }

Broken unit test:

it('should search ', function () {                
    expect(ctrl.searchMatch('numbers','one')).toBe(false);
});

This is what I get

TypeError: Object # has no method 'searchMatch'

How do you test that function? As a workaround I moved my method to $scope so I could test for scope.searchMatch but I was wondering if this is the only way.

Finally, on my tests is appears $filter is undefined too, how do you inject it? I tried this but didn't work:

ctrl = $controller('EmployeeCtrl', { $scope: scope, $filter: $filter });

Thanks

Update: The method mentioned above to inject $filter works just fine.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

My understanding is that, in Angularjs, you pass a scope object to the controller when the application is starting and the controller modify the scope. The controller is not called anymore.

What a controller is really doing, in Angularjs, is initializing the scope: the controller only runs one time. If you understand this, you realize that asking to the controller for the scope like this:

currentScope = myController.scope;

doesn't makes sense.

(Incidentally, one of the things I don't like in Angular is the names that they have choosen. If what a 'controller' is doing is initializing the scope then it's not really a controller. There are a lot of this in the Angular API).

I think that the 'proper' way for testing a 'controller' is creating a new blank scope from scratch in a Jasmine beforeEach clause and using the 'controller' for initializing a new blank scope like this::

var ctrl, myScope;

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

And then testing the new created scope has the expected properties:

it('In the scope, the initial value for a=2', function() {
            expect(myScope.a).toBe(2);
});

In other words, you don’t test the controller; you test the scope that the controller has created.

So, you're doing it right.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...