Whisher
Whisher

Reputation: 32806

karma/angularjs how to test run block with an asynchronous service

How can I test like:

init.js lama.system module

angular.module('lama.system', [])
   .config(['$httpProvider', function($httpProvider) {
        // Crossdomain requests not allowed if you want do cors request see filter.php 
        $httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
    }])
    .run(['$rootScope', '$state', '$log', 'Global',function ($rootScope, $state, $log, Global) {
        $rootScope.$state = $state;
        $rootScope.$log = $log;
        $rootScope.global = Global;
    }]);

controllers.js lama.system module

angular.module('lama.system')
    .controller('SystemController', ['$scope',
        function($scope) {
           $scope.test =[];

         }
    ]);

services.js lama.system module

angular.module('lama.system')
    .factory('Menus', ['$http', function($http) {
        return {
            query : function(menus){
                return $http.get('/api/v1/user/menus', {
                    params: {
                        'menus[]': menus
                    }
                });
            }
        }; 
    }]);

system.js router

//Setting up route
angular.module('lama.system')
    .config(['$stateProvider', '$urlRouterProvider',
        function($stateProvider, $urlRouterProvider) {
            $urlRouterProvider.otherwise('/');
            $stateProvider              
            .state('home', {
                url: '/',
                templateUrl: 'system/views/home.html',
                controller:'SystemController'
            });
        }
    ]);

init.js lama module

angular.module('lama', ['ui.router','restangular','lama.system','lama.users'])
    .run(['$rootScope',  'Menus',function ($rootScope, Menus) {
       // Default hard coded menu items for main menu
           var menus = [
            {
                'permission': null,
                'title': 'Home',
                'link': 'home'
            },
            {
                'permission': 'users',
                'title': 'User',
                'link': 'user_actions.list'
            }
            ];

            $rootScope.menus = [];

            function queryMenu(menus) {
                Menus.query(menus).then(
                    function (result) {
                        $rootScope.menus = result.data; 
                    },
                    function (reason) {
                        throw new Error(reason);
                    }
                    );
            }

            // Query server for menus and check permissions
            queryMenu(menus);

           $rootScope.$on('loggedin', function(event,user) {
                console.log('kkk');
                queryMenu(menus);

                $rootScope.global = {
                    user:  user,
                    authenticated: user.groups.length,
                    isAdmin:  user.groups.indexOf('Admins')
                };
            });


    }]);

The test

(function() {
    describe('Unit test: system module', function () {
        describe('SystemController', function () {

            // load the controller's module
            beforeEach(function() {
                module('lama');
                module('stateMock');
            });

            var SystemController,
            $rootScope,
            $scope,
            $httpBackend;

            // Initialize the controller and a mock scope
            beforeEach(inject(function ($controller, _$rootScope_, _$httpBackend_) {
                $rootScope = _$rootScope_;
                $scope = _$rootScope_.$new();
                $httpBackend = _$httpBackend_;
                SystemController = $controller('SystemController', {
                    $scope: $scope
                });
                $httpBackend.when('GET', '/api/v1/user/menus').respond(200,{});
            }));

            it('should attach a list of awesomeThings to the scope', function () {
                expect($scope.test.length).toBe(0);
            });

            it('should expose some global scope', function() {
                expect($rootScope.global).toBeTruthy();
            });
        });

    });
}());

that give me :(

grunt test
Running "karma:unit" (karma) task
INFO [karma]: Karma v0.12.21 server started at http://localhost:9876/
INFO [launcher]: Starting browser PhantomJS
WARN [watcher]: Pattern "/home/whisher/public_html/public/users/tests/unit/**/*.js" does not match any file.
INFO [PhantomJS 1.9.7 (Linux)]: Connected on socket zK1SplAjTHe4x5sAM9ew with id 16331650
WARN [web-server]: 404: /api/v1/user/menus?menus%5B%5D=%7B%22permission%22:null,%22title%22:%22Home%22,%22link%22:%22home%22%7D&menus%5B%5D=%7B%22permission%22:%22users%22,%22title%22:%22User%22,%22link%22:%22user_actions.list%22%7D
WARN [web-server]: 404: /system/views/home.html
ERROR: 'Error: [object Object]
    at http://localhost:9876/base/public/init.js?c7a481724bcf81e4810141349c93d0f698a18904:34
    at http://localhost:9876/base/public/bower_components/angular/angular.min.js?4b4cffd940f7a29168096212664973432d1d7449:100
    at http://localhost:9876/base/public/bower_components/angular/angular.min.js?4b4cffd940f7a29168096212664973432d1d7449:100
    at http://localhost:9876/base/public/bower_components/angular/angular.min.js?4b4cffd940f7a29168096212664973432d1d7449:101
    at http://localhost:9876/base/public/bower_components/angular/angular.min.js?4b4cffd940f7a29168096212664973432d1d7449:112
    at http://localhost:9876/base/public/bower_components/angular/angular.min.js?4b4cffd940f7a29168096212664973432d1d7449:109
    at http://localhost:9876/base/public/bower_components/angular/angular.min.js?4b4cffd940f7a29168096212664973432d1d7449:112
    at h (http://localhost:9876/base/public/bower_components/angular/angular.min.js?4b4cffd940f7a29168096212664973432d1d7449:72)
    at x (http://localhost:9876/base/public/bower_components/angular/angular.min.js?4b4cffd940f7a29168096212664973432d1d7449:77)
    at http://localhost:9876/base/public/bower_components/angular/angular.min.js?4b4cffd940f7a29168096212664973432d1d7449:78'
PhantomJS 1.9.7 (Linux): Executed 2 of 2 SUCCESS (0.039 secs / 0.065 secs)

Done, without errors.

NB

I'm using ui-router

Upvotes: 0

Views: 1105

Answers (1)

scarlz
scarlz

Reputation: 2512

I've taken a look at your source code. The primary offending item is:

angular.element(document).ready(function() {
  angular.bootstrap(document, ['lama']);
});

By including your manual bootstrap in the files that karma imports, your app is un-necessarily being bootstrapped within the karma test environment. So, instead, move your lama module into a separate file, and omit init.js from your files to be included by karma.

There are also a couple of other design concerns that are causing excessive test setup. When testing SystemController there is no need to include anything but the lama.system module (as this is what the controller belongs to). Your test setup for this controller can be as simple as:

var $scope, $rootScope;

beforeEach(module('system.lama'));

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

You will note that in isolating the lama.system module as above, you will get an error that $state cannot be injected (not found). This is because your module definition should instead be:

angular.module('lama.system', ['ui.router'])

(A module should not require the presence of another module for its own dependencies, else it can never function in isolation).

One final observation on your mocking for the lama module run block. Strictly speaking you should stub the service query method entirely and not just part of its implementation, else your test is completely isolated. Therefore, instead, it would be better to do the following:

beforeEach(module(function($provide) {
  $provide.decorator('Menus', function($delegate, $q) {
     // overwrite the query method
     $delegate.query = function() {
       return $q.when([{id: 1, name: 'Bob'}]);
     };    
     return $delegate; 
  });
}));

This way, you don't need to worry about using $httpBackend at all.

Upvotes: 1

Related Questions