简体   繁体   中英

Angular/Mocha/Chai - initialize controller for unit testing?

We're just getting started with unit testing in our Angular app and we are using a karma/mocha/chai framework for unit testing. I carried out some basic unit tests on various services and factories we have defined and it the unit testing works great. However now we want to test some controller and evaluate the scope vars that the controllers are modifying.

Here is an example of one such controller:

angular.module('App').controller('myCtrl', ['$scope', 'APIProxy', 
    function ($scope, APIProxy) {
        $scope.caseCounts = {caseCount1: 0, caseCount2: 0};

        $scope.applyCounts = function () {
            $scope.caseCounts.caseCount1 = {...some case count logic...}
            $scope.caseCounts.caseCount2 = {...some case count logic...}
        };

        APIProxy.getAll().then(function (data) {
            {...do a bunch of stuff...}
            $scope.data = data;
            $scope.applyCounts();
        });
    }]
);

Now, when I unit test I would like to start off with just a simple 'does the $scope.caseCounts have values > 0, then I'll build from there. However it is not obvious how to make the controller cause the APIProxy service run and how to handle the eventual return of data. We've tried $scope.getStatus(), and $scope.apply() and a few other things but I feel like we are way off the mark and we are fundamentally missing something about how to go about this.

Currently our controller tester looks like:

describe("myCtrl unit tests",function(){
    beforeEach(module('App'));

    var ctrl, APIProxy;

    beforeEach(inject(function ($rootScope, $controller, _APIProxy_)
        {
            $scope = $rootScope.$new();
            APIProxy = _APIProxy_;
            ctrl = $controller('myCtrl', {$scope: $scope, APIProxy: APIProxy});
    }));

    it('Loads data correctly', function() {
        expect(ctrl).to.not.be.undefined;
        //??? what else do we do here to fire the getAll function in controller?
    });
});

It's usually better to test the service and the controller separately.

To test the service, you can use $httpBackend to mock the XHR requests:

https://docs.angularjs.org/api/ngMock/service/$httpBackend

To test the controller, you can simply provide mocked values instead of the actual service when you initalize the controller

APIProxy = {'mocked':'data'};
ctrl = $controller('myCtrl', {$scope: $scope, APIProxy: APIProxy});

Or more generally, to mock any provider of your module:

module(function($provide) {
  $provide.constant('ab','cd');
  $provide.value('ef', 'gh');
  $provide.service('myService', function() { });
});

Which will override the 'myService' referenced as dependencies in your controller (if there is one). If you need it directly, you can then inject it too:

var myService;
beforeEach(inject(function (_myService_) {
    myService = _myService_;
}));

If you need APIProxy to return a promise, you can mock it too with https://docs.angularjs.org/api/ng/service/$q and resolve, eg:

var deferred = $q.defer();
deferred.resolve({'mocked':'data'});
return deferred.promise;

If you do want to test them together, you can do a spy on the API function you call and have the spy return a resolved promise.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM