Search code examples
javascriptangularjsunit-testingkarma-mochaangularjs-ngmock

AngularJs unit test fails because Karma doesn't recognize $scope.$on listeners in the controller


So here's the thing. I've been investigating many threads but couldn't find anything related to my error. I'm trying to test my angular app, (I want to clarify first that I'm kinda newbie to unitTesting)

According to this tutorial I've set everything up and basic tests work fine. But when I include the NgMock module, my real angular app and write a simple test to test a method on one of my controllers I get the following stackTrace.

grunt karma:dev
Running "karma:dev" (karma) task
25 03 2016 20:41:24.754:INFO [karma]: Karma v0.13.22 server started at http://localhost:9876/
25 03 2016 20:41:24.763:INFO [launcher]: Starting browser Chrome
25 03 2016 20:41:24.771:INFO [launcher]: Starting browser Firefox
25 03 2016 20:41:28.747:INFO [Chrome 49.0.2623 (Windows 10 0.0.0)]: Connected on socket /#yRHoLsbEaJWHNtSDAAAA with id 47988329
Chrome 49.0.2623 (Windows 10 0.0.0) Unit tests del CRM:  Create new message Should create a text message with the correct structure FAILED
        TypeError: $scope.$on is not a function
            at new <anonymous> (C:/Users/Pablo/Data/software/workspace/intellij/leadaki-core/public/dashboard/v4/js/controllers/view-conversations.js:248:12)
            at e (C:/Users/Pablo/Data/software/workspace/intellij/leadaki-core/public/dashboard/v4/js/angular.min.js:39:156)
            at Object.instantiate (C:/Users/Pablo/Data/software/workspace/intellij/leadaki-core/public/dashboard/v4/js/angular.min.js:39:273)
            at C:/Users/Pablo/Data/software/workspace/intellij/leadaki-core/public/dashboard/v4/js/angular.min.js:80:228
            at C:/Users/Pablo/Data/software/workspace/intellij/leadaki-core/public/dashboard/v4/js/angular-mocks.js:1848:12
            at Context.<anonymous> (C:/Users/Pablo/Data/software/workspace/intellij/leadaki-core/test/frontend/leadaki-app-tests.js:44:30)
Chrome 49.0.2623 (Windows 10 0.0.0): Executed 1 of 1 (1 FAILED) ERROR (0.087 secs / 0.024 secs)
Firefox 45.0.0 (Windows 10 0.0.0) Unit tests del CRM:  Create new message Should create a text message with the correct structure FAILED
        $scope.$on is not a function
        @C:/Users/Pablo/Data/software/workspace/intellij/leadaki-core/public/dashboard/v4/js/controllers/view-conversations.js:248:5
        e@C:/Users/Pablo/Data/software/workspace/intellij/leadaki-core/public/dashboard/v4/js/angular.min.js:39:154
        h/<.instantiate@C:/Users/Pablo/Data/software/workspace/intellij/leadaki-core/public/dashboard/v4/js/angular.min.js:39:273
        Xe/this.$get</<@C:/Users/Pablo/Data/software/workspace/intellij/leadaki-core/public/dashboard/v4/js/angular.min.js:80:226
        angular.mock.$ControllerDecorator</<@C:/Users/Pablo/Data/software/workspace/intellij/leadaki-core/public/dashboard/v4/js/angular-mocks.js:1848:12
        @C:/Users/Pablo/Data/software/workspace/intellij/leadaki-core/test/frontend/leadaki-app-tests.js:44:30

Chrome 49.0.2623 (Windows 10 0.0.0): Executed 1 of 1 (1 FAILED) ERROR (0.087 secs / 0.024 secs)
Firefox 45.0.0 (Windows 10 0.0.0): Executed 1 of 1 (1 FAILED) ERROR (0.053 secs / 0.011 secs)
Warning: Task "karma:dev" failed. Use --force to continue.

Aborted due to warnings.

It seems like the "mock" scope that ngMock generates doesn't accept to have listeners or something like that.

Does anybody have a hint of could be wrong here?

I could paste all my configuration files but I think that's too much info and I dont want to flood the post. Let me know if any other info is needed and I'll add it.

Thanks in advanced!

describe('Unit tests del CRM: ', function () {

    before(function() {
        if (window.__html__) {
            document.body.innerHTML = window.__html__['test/index.html'];
        }
    });

    beforeEach(angular.mock.module('appCliengo'));

    var $controller;
    var $rootScope;

    beforeEach(angular.mock.inject(function(_$rootScope_, _$controller_){
        $rootScope = _$rootScope_;
        $controller = _$controller_;
    }));

    describe('Create new message', function () {
        it('Should create a text message with the correct structure', function () {
            var $scope = {};
            var controller = $controller('Contacts.viewConversation', { $scope: $scope });

            var messageBody = 'Hola, me gustaria comprar un auto';
            var newTextMessage = $scope.createMessage('text/plain', '123456789', 'user', 'abcdefghijk', messageBody);

            expect(newTextMessage.body).to.equal(messageBody);
        });
    });
});

Solution

  • It's hard to say without looking at your exact setup, but did you create a scope object to inject into your controller?

    A lot of the time, you can fake it just by using the $rootScope object:

    beforeEach(angular.mock.inject($injector => {
        $controller = $injector.get('$controller');
        rootScope = $injector.get('$rootScope');
    }));
    
    ... test code ...
    
    let controller = $controller('UserController', {$scope: rootScope});
    
    ... do stuff with controller ...
    

    However, you can also use rootScope to create a new scope and feed that in.