【问题标题】:AngularJS, RequireJS and Karma issueAngularJS、RequireJS 和 Karma 问题
【发布时间】:2014-09-04 14:39:44
【问题描述】:

提前为这篇长文道歉。在过去的几天里,我一直坚持这一点。基本上,我有一个带有 RequireJS 设置的 AngularJS 项目,它运行良好。我正在尝试使用 Angular、Require、Karma (Jasmine) 组合编写单元测试。我得到的错误是:

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
....

这是文件夹结构:

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

以下是文件的相关部分:

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);
    });

});
});

我尝试按照建议使用“SampleController”,但现在我得到了:

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

我错过了什么? 非常感谢任何帮助。

【问题讨论】:

    标签: angularjs requirejs jasmine karma-runner


    【解决方案1】:

    我终于得到了这个工作 - 但对于我的生活,即使我有一个微弱的想法,我仍然无法理解它为什么工作。为任何未来迷失的灵魂张贴它。

    我必须像这样编写 SimpleController:

    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.
     });
    

    测试用例现在看起来像:

    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');
    
        });
    
    });
    });
    

    此外,SimpleController 不需要路径或垫片声明。它使用“controllers/SimpleController”在测试类中查找 - 该文件位于“app/controllers”子文件夹中。

    【讨论】:

      【解决方案2】:

      你有点搞混了:

      SimpleController.js(我猜上面的SampleController.js 是一个错字)返回名为"simpleApp"Angular 模块(这是一个对象)。在这个模块中,有一个名为"SimpleController" 的控制器。测试脚本中的正确用法是:

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

      这就是为什么 Angular 抱怨在期望函数的地方找到了一个对象。

      【讨论】:

      • 您好 Nikos,我尝试了您的建议,但这次遇到了一个新错误。我已经更新了原帖。
      • 控制器好像没有注册。你能确保 Angular 被引导(打印一条消息或其他东西)吗?文档还说:“inject() 为每个测试创建 $injector 的新实例”(ref)。会不会是 Angular 在第二次测试运行时没有正确初始化?我有同样的问题here - 请参阅XXX cmets。要解决这个问题,请保留并重复使用原始注入器。
      • 您好 Nikos,感谢您的回复。但是,我不确定在哪里发布消息(我知道我听起来很愚蠢 - 但请耐心等待 - 我对此很陌生)。此外,如果您可以更具体地说明如何保留和重复使用原始注射器 - 那会很棒。非常感谢!
      • 如果您手动引导 Angular,您可以保留从 inj = angular.bootstrap(document, ["simpleApp"]) 返回的值。如果 Angular 是自动引导的,你可以使用 angular.element(...element with ng-app...).injector() 获取注入器。
      猜你喜欢
      • 1970-01-01
      • 2018-02-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-03
      • 2014-06-24
      • 1970-01-01
      • 2014-07-18
      相关资源
      最近更新 更多