steveareeno
steveareeno

Reputation: 1977

AngularJS + Jasmine unit test Error: [$injector:modulerr]

I am leanring unit testing with angular and jasmine. I am getting the following error when running the tests:

Result Message: Error: [$injector:modulerr] http://errors.angularjs.org/1.4.9/$injector/modulerr...

Unit Test

/// <reference path="../Scripts/_references.js" />
/// <reference path="jasmine.js" />
/// <reference path="SampleController.js" />

describe('Controller Unit Test: ', function () {
    var scope, ctrl, $timeout;
    var someServiceMock;

    beforeEach(function () {
        module('app');
    });

    beforeEach(function () {
        someServiceMock = jasmine.createSpyObj('someService', ['someAsyncCall']);
        inject(function ($rootScope, $controller, $q, _$timeout_) {
            scope = $rootScope.$new();
            someServiceMock.someAsyncCall.andReturn($q.when('weee'));
            $timeout = _$timeout_;
            ctrl = $controller('SampleController', {
                $scope: scope,
                someService: someServiceMock
            });
        });
    });

    it('Controller exists', function () {
        expect(ctrl).toBeDefined();
    });
});

Controller

var app = angular.module('myApp', []);
app.controller('SampleController', function ($scope, someService) {

    //set some properties
    $scope.foo = 'foo';
    $scope.bar = 'bar';

    //add a simple function.
    $scope.test1 = function () {
        $scope.foo = $scope.foo + '!!!';
    };

    //set up a $watch.
    $scope.$watch('bar', function (v) {
        $scope.baz = v + 'baz';
    });

    //make a call to an injected service.
    $scope.test2 = function () {
        someService.someAsyncCall($scope.foo)
          .then(function (data) {
              $scope.fizz = data;
          });
    };
});

app.factory('someService', function ($timeout, $q) {
    return {

        // simple method to do something asynchronously.
        someAsyncCall: function (x) {
            var deferred = $q.defer();
            $timeout(function () {
                deferred.resolve(x + '_async');
            }, 100);
            return deferred.promise;
        }
    };
});

finally, my reference files:

/// <reference path="angular.min.js" />
/// <reference path="angular-mocks.js" />
/// <reference path="angular-animate.min.js" />
/// <reference path="angular-sanitize.min.js" />
/// <reference path="angular-route.min.js" />
/// <reference path="jquery-2.1.1.min.js" />
/// <reference path="jquery.unobtrusive-ajax.js" />
/// <reference path="jquery.unobtrusive-ajax.min.js" />
/// <reference path="jquery.validate.min.js" />
/// <reference path="ui-bootstrap-tpls-0.14.3.min.js" />
/// <reference path="modernizr-2.8.3.js" />

When I set a break point within the inject function in the test and run debugging mode, it nevers goes into it.

Upvotes: 1

Views: 1810

Answers (1)

Igor
Igor

Reputation: 62213

Try modifying your test code like so, I also took the liberty of modying your structure so that way your tests might be a bit more efficient. For smaller controllers / tests it does not matter but for more complex functionality the ability to reset for a test set makes this easier.

The 1st beforeEach will inject $injector into the function which you can use to get your references. This can be done in the main describe function as they will likely not change.

The 2nd beforeEach I have nested in a new describe function. You can then create a batched set of tests where each it is guaranteed a fresh/new controller instance.

If $injector is null or undefined it means that the scripts are not being loaded or being loaded in the wrong order. To better troubleshoot this you need to see what is failing the injection because Angular is not yet aware of it, in that case please comment with the full message/url like you started to at the top of your post and we can then put the references in the correct order.

Controller file

/// <reference path="jquery-2.1.1.min.js" />
/// <reference path="angular.min.js" />
... rest of your code

Unit test file

/// <chutzpah_reference path="angular.min.js"/>
/// <chutzpah_reference path="angular-mocks.js"/>
// you might need additional chutzpah_reference but it really depends on your chutzpah configuration found in chutzpah.json
/// <reference path="jquery-2.1.1.min.js" />
/// <reference path="angular.min.js" />
/// <reference path="angular-mocks.js" />
/// <reference path="jasmine.js" />
/// <reference path="SampleController.js" />

describe('Controller Unit Test: ', function () {
    var $rootScope;  //, $timeout;
    var someServiceMock;

    beforeEach(function () {
        angular.mock.module("app"); // mock your module
    });

    // setup
    beforeEach(inject(function ($injector) {
        someServiceMock = jasmine.createSpyObj('someService', ['someAsyncCall']);
        someServiceMock.someAsyncCall.andReturn($q.when('weee'));

        var $rootScope = $injector.get("$rootScope");
        // $timeout = $injector.get("$timeout"); // not used anywhere that I can see
    }));

    describe('this will executes initial some set of tests on your controller', () => {
        var scope;
        var ctrl;
        beforeEach(inject(function ($controller) {
            scope = $rootScope.$new();
            ctrl = $controller('SampleController', {
                $scope: scope,
                someService: someServiceMock});
        }));

        it('Controller exists', function () {
            expect(scope).toBeDefined();
            expect(ctrl).toBeDefined();
        });
    });
});

Edit 1

Changed references. Added angular mock call.

Upvotes: 0

Related Questions