trishulpani
trishulpani

Reputation: 754

AngularJS, RequireJS and Karma issue

Apologies in advance for the long post. I have been stuck with this for the last couple of days. Basically, I have an AngularJS project with RequireJS setup which works fine. I am trying to write unit tests using the Angular, Require, Karma (Jasmine) combo. The error am getting is:

Error: [ng:areq] Argument 'fn' is not a function, got Object
http://errors.angularjs.org/1.2.9/ng/areq?p0=fn&p1=not%20a%20function%2C%20got%20Object
at D:/workspace/ecart-oauth/src/main/webapp/app/lib/angular/angular.js:78:12
at assertArg (D:/workspace/ecart-oauth/src/main/webapp/app/lib/angular/angular.js:1363:11)
at assertArgFn (D:/workspace/ecart-oauth/src/main/webapp/app/lib/angular/angular.js:1373:3)
at annotate (D:/workspace/ecart-oauth/src/main/webapp/app/lib/angular/angular.js:3019:5)
at invoke (D:/workspace/ecart-oauth/src/main/webapp/app/lib/angular/angular.js:3687:21)
at Object.instantiate (D:/workspace/ecart-oauth/src/main/webapp/app/lib/angular/angular.js:3721:23)
at D:/workspace/ecart-oauth/src/main/webapp/app/lib/angular/angular.js:6772:28
at null.<anonymous> (D:/workspace/ecart-oauth/src/main/webapp/test/spec/TestAppSetupSpec.js:11:24)
at Object.invoke (D:/workspace/ecart-oauth/src/main/webapp/app/lib/angular/angular.js:3710:17)
at workFn (D:/workspace/ecart-oauth/src/main/webapp/app/lib/angular/angular-mocks.js:2149:20)
Error: Declaration Location
at window.inject.angular.mock.inject (D:/workspace/ecart-oauth/src/main/webapp/app/lib/angular/angular-mocks.js:2134:2

at null.<anonymous> (D:/workspace/ecart-oauth/src/main/webapp/test/spec/TestAppSetupSpec.js:9:20)
at D:/workspace/ecart-oauth/src/main/webapp/test/spec/TestAppSetupSpec.js:4:5
....

This is the folder structure:

ROOT
|_app
|   |_js
|   |    |_controllers
|   |         |_SimpleController.js
|   |_app.js, main.js
|
|_test
|     |_spec
|           |_mockApp.js
|           |_TestAppSetupSpec.js
|
|__karma.conf.js
|_ test-main.js

Here are the relevant sections of the files:

SimpleController.js:

define(["app"], function( app ) {

return angular.module("simpleApp").controller("SimpleController", ["$scope",
    function($scope) {
       $scope.msg = "Hello ";
    }
]);
});

karma.conf.js

....
  // list of files / patterns to load in the browser
files: [

  {pattern: 'app/lib/**/*.js', included: false},
  {pattern: 'app/js/controllers/*.js', included: false},
  {pattern: 'app/js/**/*.js', included: false},
  {pattern: 'app/js/services/*.js', included: false},
  {pattern: 'test/spec/*Spec.js', included: false},
  {pattern: 'test/spec/mockApp.js', included: false},
  'test-main.js',
],


// list of files to exclude
exclude: [
  'app/js/main.js', 'app/js/app.js'    ],
....

test-main.js

var tests = [];
for (var file in window.__karma__.files) {
if (window.__karma__.files.hasOwnProperty(file)) {
if (/Spec\.js$/.test(file)) {
  tests.push(file);
}
}
}

require.config({
// Karma serves files under /base, which is the basePath from your config file
baseUrl: '/base/app/js',

paths: {
    "angular" : "../lib/angular/angular",
    "angular-resource" : "../lib/angular/angular-resource",
    "angular-route" : "../lib/angular/angular-route",
    "angular-storage" : "../lib/angular-local-storage",
    "bootstrap-tooltip" : "../lib/bootstrap-tooltip",
    "bootstrap-popover" : "../lib/bootstrap-popover",
    "bootstrap" : "../lib/bootstrap.min",
    "ui-bootstrap-tpls" : "../lib/ui-bootstrap-tpls-0.11.0",
    "dirPagination" : "../lib/dirPagination",
    "translate" : "../lib/angular-translate.min",
    "translationsEN" : "../js/locale/en/translations_en",
    "translationsDE" : "../js/locale/de/translations_de",
    "jQuery" : "../lib/jquery-1.9.0",
    "angular-mocks" : "../lib/angular/angular-mocks",
    "app" : "../../test/spec/mockApp",
    "SimpleController" : "controllers/SimpleController"
},
shim: {

    "jQuery" :{
        exports : 'jQuery'
    },

    "angular" : {
        deps: ["jQuery"],
        exports: 'angular'
    },


    "angular-resource": {
            deps: ["angular"],
            exports : 'ngResource'
    },


    "app" : {
         exports : 'app'
    },

    "SimpleController" : {
        deps : ["app"],
        exports : 'SimpleController'
    },


    "angular-mocks" : {

       deps : ["angular"],
       exports : 'angular-mocks'
     },

    "angular-route": {
      deps: ["angular"],
      exports : 'ngRoute'
  },
  "angular-storage": {
      deps: ["angular"],
      exports : 'angular-storage'
  },
  "bootstrap": {
     deps: ["jQuery"],
        exports : 'bootstrap'

   },
  "bootstrap-tooltip": {
     exports : 'bootstrap-tooltip',
     deps : ["jQuery"]
  },
  "bootstrap-popover": {
      exports : 'bootstrap-popover',
      deps : ["jQuery"]
  },

  "ui-bootstrap-tpls": {
    deps : ["jQuery", "angular", "bootstrap", "bootstrap-tooltip", "bootstrap-popover"],
    exports : 'ui-bootstrap-tpls'
  },
  "dirPagination": {
    deps : ["angular"],
      exports : 'dirPagination'
  },
  "translate": {
    deps : ["angular", "translationsEN", "translationsDE"],
      exports : 'translate'
  }

},
// dynamically load all test files
deps: tests,

// we have to kickoff jasmine, as it is asynchronous
callback: window.__karma__.start
});

mockApp.js :

define(["angular"], function(){ 
    var app = angular.module('simpleApp',[]);
    return app;

});

TestAppSetupSpec.js :

define([ 'angular', 'angular-mocks','SimpleController'], function (
     angular, angularMocks, SimpleController
    ) {
describe('App module tests', function () {
    var module, $rootScope, scope, AppCtrl;

    beforeEach(angular.module("simpleApp"));

    beforeEach(inject(function ($rootScope, $controller) {
        scope = $rootScope.$new();
        AppCtrl =  $controller("SimpleController", {
                '$scope': scope
        });

    }));

    it("App Controller should be defined", function(){

        expect(AppCtrl).not.toBe(null);
    });

});
});

I tried using "SampleController" as suggested, but now I get:

Chrome 37.0.2062 (Windows 7): Executed 2 of 2 (1 FAILED) (0.026 secs / 0.023 secs)
INFO [watcher]: Changed file "d:/workspace/ecart-   oauth/src/main/webapp/test/spec/TestAppSetupSpec.js".
Chrome 37.0.2062 (Windows 7) App module tests App Controller should be defined FAILED
    TypeError: undefined is not a function
    Error: [ng:areq] Argument 'SimpleController' is not a function, got undefined
    http://errors.angularjs.org/1.2.9/ng/areq?  p0=SimpleController&p1=not%20a%20function%2C%20got%20undefined
        at d:/workspace/ecart-oauth/src/main/webapp/app/lib/angular/angular.js:78:12
        at assertArg (d:/workspace/ecart-oauth/src/main/webapp/app/lib/angular/angular.js:1363:11)
        at assertArgFn (d:/workspace/ecart-oauth/src/main/webapp/app/lib/angular/angular.js:1373:3)
        at d:/workspace/ecart-oauth/src/main/webapp/app/lib/angular/angular.js:6769:9
        at null.<anonymous> (d:/workspace/ecart-oauth/src/main/webapp/test/spec/TestAppSetupSpec.js:12:26)
        at Object.invoke (d:/workspace/ecart-oauth/src/main/webapp/app/lib/angular/angular.js:3710:17)
        at workFn (d:/workspace/ecart-oauth/src/main/webapp/app/lib/angular/angular-mocks.js:2149:20)
    Error: Declaration Location
        at window.inject.angular.mock.inject (d:/workspace/ecart-oauth/src/main/webapp/app/lib/angular/angular-mocks.js:2134:2
5)
        at null.<anonymous> (d:/workspace/ecart-oauth/src/main/webapp/test/spec/TestAppSetupSpec.js:9:20)
        at d:/workspace/ecart-oauth/src/main/webapp/test/spec/TestAppSetupSpec.js:4:5

What am I missing? Any help is much appreciated.

Upvotes: 1

Views: 1096

Answers (2)

trishulpani
trishulpani

Reputation: 754

I finally got this working - but for the life of me, I still can't understand why it works even though I have a faint idea. Posting it for any future lost souls.

I had to write the SimpleController like :

define(["app"], function(app) {

var SimpleController = 
    function($scope) {
        $scope.sayHello = function() {
            return "Hello";
        }
    }


SimpleController.$inject = ['$scope'];


app.controller('SimpleController', SimpleController);

return SimpleController;  //NOTE: not returning app.controller(..) which returns an object.
 });

The test case now looks like:

define([ 'angular', 'angular-mocks', 'controllers/SimpleController'], function (
     angular, angularMocks, SimpleController
    ) {
describe('App module tests -->', function () {
    var  scope, SimpleCtrl;


    beforeEach(
            function(){
             angular.module("simpleApp");
            }

    );

    beforeEach(inject(function($rootScope, $controller){

        scope = $rootScope.$new();

        SimpleCtrl =  $controller(SimpleController, {
             '$scope': scope
         });
    })

    );

    it("App Controller should be defined", function(){

        expect(SimpleCtrl).not.toBe(null);
    });

    it("The controller should have a sayHello method that says Hello", function(){

        expect(scope.sayHello).toMatch('Hello');

    });

});
});

Also, no path or shim declarations for SimpleController are required. Its looked up in the test class using 'controllers/SimpleController' - the file resides in the 'app/controllers' subfolder.

Upvotes: 1

Nikos Paraskevopoulos
Nikos Paraskevopoulos

Reputation: 40296

You have mixed things up a bit:

SimpleController.js (I guess SampleController.js above is a typo) returns the Angular module named "simpleApp" (which is an object). In this module, there is a controller called "SimpleController". The correct usage in the test script would be:

AppCtrl =  $controller("SimpleController", { // NOTE THE QUOTES!!!
    '$scope': scope
});

This is why Angular is complaining that an object was found where a function was expected.

Upvotes: 0

Related Questions