Search code examples
angularjskarma-runnerangular-mock

karma/angularjs how to test run block with service


This post follow this I've posted an other thread with a easier example

code to test

 'use strict';

    var app = angular.module('myApp', []);
    app.run(function ($rootScope, Menus) {

        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);
                }
                );
        }
        $rootScope.valueSetInRun = 555;
        queryMenu(menus);
    });
    app.factory('Menus',  function($http) {
        return {
            query : function(menus){
                return $http.get('menus.json');
            }
        }; 
    });
    app.factory('UserFactory', function($http){
        return $http.get('users.json')
    });
    app.controller('MainCtrl', function($scope, UserFactory) {
        $scope.users = [];
        $scope.text = 'Hello World!';
        UserFactory.then(function(data){
            $scope.users =  data.data;
        });
    });

test

'use strict';

describe('Run', function() {
    beforeEach(module('myApp'));
   var $httpBackend;
    beforeEach(inject(function($rootScope,_$httpBackend_) {
        $httpBackend = _$httpBackend_;
        console.log('beforeEach');
    }));

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

    it('should allow me to test the run() block', inject(function ($rootScope,$httpBackend) {
        console.log('it block');
        // Doesn't work take a look at https://stackoverflow.com/questions/25363969/karma-angularjs-how-to-test-run-block-with-an-asynchronous-service
        $httpBackend.when('GET', 'menus.json').respond([{id: 1, name: 'Bob'}, {id:2, name: 'Jane'}]);
       $httpBackend.flush();
       expect( $rootScope.valueSetInRun ).toBe(555);
       console.log($rootScope.menus);
  }));
}); 

describe('MainCtrl', function(){
    var scope, $httpBackend;//we'll use these in our tests

    //mock Application to allow us to inject our own dependencies
    beforeEach(module('myApp'));
    //mock the controller for the same reason and include $rootScope and $controller
    beforeEach(inject(function( $controller,_$rootScope_, _$httpBackend_){
        $httpBackend = _$httpBackend_;

        $httpBackend.when('GET', 'users.json').respond([{id: 1, name: 'Bob'}, {id:2, name: 'Jane'}]);

        //create an empty scope
        scope = _$rootScope_.$new();
        //declare the controller and inject our empty scope
        $controller('MainCtrl', {$scope: scope});
        $httpBackend.flush();
    }));

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

    it('should have variable text = "Hello World!"', function(){
       expect(scope.text).toBe('Hello World!'); 
    });

    it('should fetch list of users', function(){
       //expect(scope.users.length).toBe(2);
       //expect(scope.users[0].name).toBe('Bob');
    });
});

that give me this error

**Error: Unexpected request: GET menus.json
No more request expected**

END UP see the reply of @scarIz

beforeEach(module('myApp'));
var $httpBackend;
beforeEach(inject(function( _$httpBackend_) {
    $httpBackend = _$httpBackend_;
    $httpBackend.when('GET', 'menus.json').respond([{
        id: 1, 
        name: 'Bob'
    }])
    $httpBackend.flush();
}));
afterEach(function() {
    $httpBackend.verifyNoOutstandingExpectation();
    $httpBackend.verifyNoOutstandingRequest();
});
describe('Init', function() {

    describe('MainCtrl', function(){
        var scope, $httpBackend,$rootScope;//we'll use these in our tests

        //mock Application to allow us to inject our own dependencies

        //mock the controller for the same reason and include $rootScope and $controller
        beforeEach(inject(function( $controller,_$rootScope_, _$httpBackend_){
            $httpBackend = _$httpBackend_;

            $httpBackend.when('GET', 'users.json').respond([{
                id: 1, 
                name: 'Bob'
            }, {
                id:2, 
                name: 'Jane'
            }]);
            $rootScope = _$rootScope_;
            //create an empty scope
            scope = _$rootScope_.$new();
            //declare the controller and inject our empty scope
            $controller('MainCtrl', {
                $scope: scope
            });
            $httpBackend.flush();
        }));

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

        it('should have variable text = "Hello World!"', function(){
            expect(scope.text).toBe('Hello World!'); 
        });

        it('should fetch list of users', function(){
            expect(scope.users.length).toBe(2);
            expect(scope.users[0].name).toBe('Bob');
             expect( $rootScope.valueSetInRun ).toBe(555);
           console.log($rootScope.menus);
        });
    });
}); 

Solution

  • The reason for your error is that Menus.query will run before every test you perform for the myApp module as it's called in the module's .run() configuration. While you have provided a backend definition in your Run test suite, you have not done so for MainCtrl.

    Therefore, to counter this, you might want to use a global beforeEach block that runs before every test, which should be defined in a helper file to be included prior to your specs:

    beforeEach(inject(function($httpBackend) {
      $httpBackend.when('GET', 'menus.json').respond([{id: 1, name: 'Bob'}])
      $httpBackend.flush();
    }));