【问题标题】:Unit testing angularjs controller using Node.js, Gulp.js and Mocha使用 Node.js、Gulp.js 和 Mocha 对 angularjs 控制器进行单元测试
【发布时间】:2014-12-23 00:18:17
【问题描述】:

我正在尝试使用 Node.js 对 angularjs 控制器进行单元测试。我正在使用 gulp.js 和 mocha 通过 gulp-mocha 运行测试。

这就是我的gulpfile.js 现在的样子:

(function () {

    var gulp = require('gulp');
    var mocha = require('gulp-mocha');

    gulp.task('default', function () {
        return gulp
            .src('Scripts/Tests/*.js', { read: false })
            .pipe(mocha({ 
                ui: 'tdd', 
                reporter: 'dot',
                globals: {
                    angular: require('./Scripts/angular.js')
                }
            }));
    });

})();

这是正在测试的代码:

(function () {

    var application = angular.module('Main');

    application.controller('MainController', ['$scope', function ($scope) {

        $scope.isDisabled = function () {
            return true;
        };

    }]);

})();

这是测试文件本身:

(function () {

    var chai = require('chai');
    var assert = chai.assert;

    angular.module('Main', []);  // Create an empty module  
    require('../MainController.js');  // Fill the module with the controller

    suite('Main Controller', function () {

        test('isDisabled always true', function () {

            var controllerFactory = angular.injector.get('$controller');
            var controller  = controllerFactory('MainController', {
                '$scope': {}
            });

            var result = controller.isDisabled();
            assert.isTrue(result);
        });

    });

})();

为了让我的测试和我正在测试的文件能够正常工作,我需要将 angular 设为全局。但是,在gulpfile.js 中对require 的调用给了我一个Reference Error: window is not defined 错误。这是有道理的,因为我在 Node.js 中运行。

在 Node.js 中加载 Angular 需要做什么?

【问题讨论】:

    标签: angularjs node.js unit-testing mocha.js gulp


    【解决方案1】:

    正如@unobf 所说(以及 angularjs 文档所说),从 Node.js 测试 angular 的诀窍是使用 Karma。这意味着通过npm install 安装karmakarma-mocha,以及karma-chrome-launcherkarma-ie-launcherkarma-firefox-launcher(或其他)。

    然后我基本上不得不从头开始重做我的gulp.js 文件:

    (function () {
    
        var gulp = require('gulp');
        var karma = require('karma').server;
    
        gulp.task('runTests', function (done) {
            karma.start({
                configFile: __dirname + '/karma.config.js',
                singleRun: true
            }, done);
        });
    
        gulp.task('default', ['runTests']);
    
    })();
    

    我还必须创建一个karma.config.js 文件来配置 karma 以使用 Chrome、Firefox、IE 和 mocha。在同一个文件中,我将 mocha 配置为使用 tdd UI 和点报告器:

    module.exports = function(config) {
        config.set({
            browsers: ['Chrome', 'Firefox', 'IE'],
            frameworks: ['mocha'],
            files: [
                './Scripts/angular.js',
                './Scripts/chai.js',
                './Scripts/*.js',
                './Scripts/Tests/*.js'
            ],
            singleRun: true,
            client: {
                mocha: {
                    reporter: 'dot',
                    ui: 'tdd'
                }
            }
        });
    };
    

    我了解到您必须首先指定 angularjs、chai.js 等,以便首先在其他文件的全局范围内拾取它们。同样,被测试代码的文件必须列在测试文件之前。

    被测试的代码根本没有改变。一旦我的测试开始运行,我意识到我的测试被破坏了,它最终看起来像这样通过了:

    (function () {
    
        var assert = chai.assert;
    
        suite('Main Controller', function () {
    
            test('isDisabled always true', function () {
    
                var injector = angular.injector(['ng', 'Main']);
                var $controller = injector.get('$controller');
                var scope = {};
                var controller  = $controller('MainController', {
                    '$scope': scope
                });
    
                var result = scope.isDisabled();
                assert.isTrue(result);
            });
    
        });
    
    })();
    

    最大的收获是控制器只是填充了$scope 对象。我改为在 $scope 对象上调用 isDisabled()。当然,拿到控制器的工作量很大,所以使用angular-mocks.js提供的inject方法更有意义:

    (function () {
    
        var assert = chai.assert;
    
        suite('Main Controller', function () {
    
            var $scope = {};
    
            setup(function () {
                module('Main');
                inject(function ($controller) {
                    $controller('MainController', {
                        '$scope': $scope
                    });
                });
            });
    
            test('isDisabled always true', inject(function ($controller) {          
                var result = $scope.isDisabled();
                assert.isTrue(result);
            }));
    
        });
    
    })();
    

    希望这足以让任何尝试使用 Node.js 和 mocha 测试 angularjs 代码的人开始。

    【讨论】:

    • 感谢您的详尽解释。
    【解决方案2】:

    我在 Grunt 中这样做,但同样的原则也适用于 Gulp。您需要注入“angular-mocks.js”文件才能模拟依赖注入。我正在使用 Karma http://karma-runner.github.io/0.12/index.html 来执行此操作,并将其设置在我的 karma.conf.js 文件中,如下所示:

    module.exports = function(config){
      config.set({
        basePath : '../../',
    
        files : [
          'bower_components/angular/angular.js',
          'bower_components/angular-mocks/angular-mocks.js',
          ...
    

    然后您可以将 $controller 注入到您的测试中,并执行诸如测试您的控制器初始化之类的操作。下面的测试是初始化作用域,然后测试控制器是否正在向作用域添加方法(注意,我使用的是 Jasmine,但同样可以使用 mocha 完成),但 Jasmine 带有一些不错的内置间谍功能。

    describe('analysisController', function () {
        var scope, state;
    
        beforeEach(function () {
            module('analysis');
            scope = {
                $apply:jasmine.createSpy(),
                ws: {
                    registerCallback:jasmine.createSpy(),
                    sendMessage:jasmine.createSpy()
                }
            },
            state = {
                go : jasmine.createSpy()
            };
        });
        it('should add a bunch of methods to the scope', inject(function ($controller) {
            $controller('analysisController', {
                $scope : scope,
                $state: state
            });
            expect(typeof scope.colorContrast).toBe('function');
            expect(typeof scope.XPathFromIssue).toBe('function');
        }));
        ...
    

    【讨论】:

    • 你能告诉我全球inject来自哪里吗?有没有其他方法可以访问$controller
    • 我最终只是通过angular.injector(['ng', 'Main']).get('$controller') 创建了一个注入器。不过,我会在我的实际项目中改用 angular-mock。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-05-01
    • 2016-02-12
    • 2014-04-01
    • 2014-08-07
    • 1970-01-01
    • 2021-09-27
    • 1970-01-01
    相关资源
    最近更新 更多