Reputation: 754
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
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
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