【问题标题】:KarmaJS, Jasmine, RequireJS, etc: How to Use Require for Testing ModulesKarmaJS、Jasmine、RequireJS 等:如何使用 Require 测试模块
【发布时间】:2015-01-18 22:32:06
【问题描述】:

使用 RequireJS 运行 Karma + Jasmine 测试 -- 起步

救命! . . . _ _ _ 。 . .求救!

目前,我有一个练习项目来熟悉 KarmaJS 以及整个单元测试。广泛的问题是,我真的对 Karma 在幕后所做的事情没有透明的看法,而且我似乎无法在相关领域找到足够的文档。事不宜迟……

这是我的文件夹结构

root
    |-/lib
        |-/[dependencies] (/angular, /angular-mocks, /bootstrap, /etc)  # from bower
    |-/src                                                              
        |-/[unreferenced directories] (/js, /css, /views)               # not referenced anywhere
        |-app.js                                                        # sets up angular.module('app', ...)
        |-globals.js                                                    # may be referenced in RequireJS main file; not used.
        |-index.html                                                    # loads bootstrap.css and RequireJS main file
        |-main.js                                                       # .config + require(['app', 'etc'])
        |-routeMap.js                                                   # sets up a single route
        |-test-file.js                                                  # *** simple define(function(){ return {...}; })
    |-/test
        |-/spec
            |-test-test-file.js                                         # *** require || define(['test-file'])
    |-.bowerrc                                                          # { "directory": "lib" }
    |-bower.json                                                        # standard format
    |-karma.conf.js                                                     # *** HELP!
    |-test-main.js                                                      # *** Save Our Souls!!!

karma.conf.js

// Karma configuration
// Generated on Wed Nov 19 2014 15:16:56 GMT-0700 (Mountain Standard Time)

module.exports = function(config) {
  config.set({

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: '',


    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['jasmine', 'requirejs'],


    // list of files / patterns to load in the browser
    files: [
      //'test/spec/test-test-file.js',
      //'lib/**/*.js',
      //'src/**/*.js',
      //'test/spec/**/*.js',
      'test-main.js',
      {pattern: 'lib/**/*.js', included: false},
      {pattern: 'src/**/*.js', included: false},
      {pattern: 'test/spec/*.js', included: true}
    ],


    // list of files to exclude
    exclude: [
        'lib/**/!(angular|angular-mocks|angular-resource|angular-route|require|text).js',
        'lib/**/**/!(jquery|bootstrap).js',
        'src/app.js'
    ],


    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
    },


    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['progress'],


    // web server port
    port: 9876,


    // enable / disable colors in the output (reporters and logs)
    colors: true,


    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,


    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: true,


    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: ['Chrome'],


    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: false
  });
};

test-main.js

var allTestFiles = [];
var TEST_REGEXP = /(spec|test)\.js$/i;

var pathToModule = function(path) {
    return path.replace(/^\/base\//, '').replace(/\.js$/, '');
};

Object.keys(window.__karma__.files).forEach(function(file) {
    if (TEST_REGEXP.test(file)) {
        // Normalize paths to RequireJS module names.
        allTestFiles.push(pathToModule(file));
    }
});

require.config({
    // Karma serves files under /base, which is the basePath from your config file
    baseUrl: '/base/src',
    
    paths: {
        
        angular: '../lib/angular/angular',
        ngRoute: '../lib/angular-route/angular-route',
        jquery: '../lib/jQuery/dist/jquery',
        bootstrap: '../lib/bootstrap/dist/js/bootstrap',
        models: 'models',
        controllers: 'controllers',
        globals: 'globals',
        routeMap: 'routeMap'
    },
    shim: {
        angular: {
            exports: 'angular'
        },
        ngRoute: {
            deps: ['angular']
        },
        jquery: {
            exports: '$'
        },
        bootstrap: {
            deps: ['jquery']
        }
    },

    // dynamically load all test files
    deps: allTestFiles,

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

test-test-file.js

console.log('....................');

define(function(){
    //console.log('testing test-file', testFile);
    
    describe('Testing testing', function(){
        it('should work', function(){
            expect(true).toEqual(true);
        });
    });

});

test-file.js

define('testFile', [], function(){
    
    return function init(sandbox){
        var app, application = app = sandbox.app
          , globals = sandbox.globals;
        
        return {
            some: 'module'
        };
    };
    
});

问题和说明

我希望听到答案的关键点是

  • {pattern: '...', include: true|false } 有什么作用?
  • 排除 bower 目录中所有额外内容的最佳方法。
  • 我需要在 test-main.js 文件中包含哪些文件?
  • 我需要在 karma.conf.js 文件中包含哪些文件?
  • test-main.js 实际上做了什么;有什么用?

我收到错误和问题的时间是在我将规范包装在 define(...) 调用中时——当我给模块一个 ID 时发生的事件——define('someId', function(){ ... })——我需要返回一些东西吗在这个模块之外,因为它是一个define 调用?

其他时候,我收到'ol 错误:'/base/src/app.js 没有时间戳!'。 “当然是时间戳!我真是太傻了……”——这到底是什么意思?!有时我会收到臭名昭著的“Executed 0 of 0 ERROR”——我也可以在这里澄清一下,拜托。真的,我得到了很多 ERROR: '...no timestamp...' 错误——甚至404s 当它似乎我应该拉那个库与 karma.conf.js files 配置...???

似乎通常当我明确告诉 karma 排除src/app.js 时,我仍然会收到404s 和错误。

tl;dr

显然,我对 Karma 和 *DD 大体上是一个有点困惑的新手......

当我的 karma.conf.js files 数组看起来像 [ 'test-main.js', 'test/spec/test-test-file.js' ] 时,我可以很好地运行 test-test-file.js -- 但是,如果我将测试包装在 RequireJS 定义调用中,我会得到“不匹配匿名定义()”上面提到的错误。

似乎当我添加 { pattern: '...', include: false } 然后业力不会为给定的文件添加我的任何文件任何模式(???)。

如果有人甚至可以简单地指导我如何将 RequireJS 与 Karma 一起使用——也就是说,我可以将我的测试包装在一个 define/require 调用中并拉入我想要测试的模块... 那将不胜感激。

由于这些类型的问题要简短且仍能提供足够的信息有些困难,我希望我没有把它弄得太长。


编辑

在阅读了 glepretre 的答案和我自己的一些摆弄之后,我重新配置了我的项目如下:

  • test-main.js 移至test/test-main.js
  • test-test-file.js 重命名为 testFileSpec.js -- 将其从 test/spec 移至 test/

karma.conf.js:

...
// list of files / patterns to load in the browser
files: [
    {pattern: 'lib/**/*.js', included: false},
    {pattern: 'src/**/*.js', included: false},
    {pattern: 'test/**/*Spec.js', included: false},
    
    'test/test-main.js'
  
],
....

test/test-main.js:

/* **************** HOW COME THE DEFAULT (Karma-generated) CONFIGURATION DOES ***NOT WORK???
var allTestFiles = [];
var TEST_REGEXP = /(spec|test)\.js$/i;

var pathToModule = function(path) {
    return path.replace(/^\/base\//, '').replace(/\.js$/, '');
};

Object.keys(window.__karma__.files).forEach(function(file) {
    if (TEST_REGEXP.test(file)) {
        // Normalize paths to RequireJS module names.
        allTestFiles.push(pathToModule(file));
    }
});
*/

var tests = [];
for (var file in window.__karma__.files) {
    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/src',
    
    paths: {},
    shim: {},

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

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

我现在正在成功运行单元测试! 特别感谢 glepretre 和所有其他贡献者


感谢您的任何见解:)

【问题讨论】:

    标签: unit-testing requirejs jasmine karma-runner karma-jasmine


    【解决方案1】:

    好的,我将尝试一次解决每个问题:

    问题 1

    • {pattern: '...', included: true|false } 有什么作用?

    Karma 的默认行为是:

    • 查找与pattern(强制属性)匹配的所有文件
    • 观察它们的变化(watched 选项),以便在您编辑代码时重新启动单元测试以提供实时结果(只有在您保留默认 autoWatch 默认时才有效值true)。
    • 使用自己的网络服务器为他们提供服务(served 选项)
    • 使用<script>included选项)将它们包含在浏览器中

    因此,在 karma 配置的 files 数组中,您可以通过仅添加字符串模式来使用默认行为:

    files: [
      // this will match all your JS files 
      // in the src/ directory and subdirectories
      'src/**/*.js'
    ]
    

    或者使用完整的对象语法来自定义每个选项:

    files: [
      {pattern: 'src/**/*.js', watched: true, served: true, included: false}
    ]
    

    使用 requireJS,你不希望它们被包含,因为它会与 requireJS 的行为冲突!

    包括在内。说明: 是否应该使用<script> 标签将文件包含在浏览器中?如果您想手动加载它们,请使用 false,例如。使用 Require.js

    来自karma/config/files docs

    注意:注意在数组中添加文件/模式的顺序。很重要!如需更多了解,请在您的 karma 配置中设置 logLevel: config.LOG_DEBUG

    问题 2

    • 我需要在 karma.conf.js 文件中包含哪些文件?

    至少所有必需的文件都可以让您的组件正常运行以进行单元测试。

    基本上,您的define([])require() 块中列出的所有文件。

    问题 3

    • 排除 bower 目录中所有额外内容的最佳方法。

    你到底想做什么?

    根据我之前写的内容,您可以看到您可以选择性地添加测试中需要的文件。

    当我的凉亭包使用模板时,我习惯添加模式'/bower_components/**/*.js' 甚至'/bower_components/**/*.html'。如果这是您所担心的,我从未注意到任何重大的性能问题...由您来定义您需要的文件模式。

    问题 4 和 5

    • test-main.js 实际上做了什么;这是干什么用的?

    • 我需要在 test-main.js 文件中包含哪些文件?

    test-main.js 文件的目的是在启动 Karma 之前查找并加载您的测试文件。它连接了 Karma 和 requireJS 之间的点

    您必须选择一个约定来命名您的测试文件,然后定义 TEST_REGEXP 以匹配所有这些文件。

    “官方”angular style guide and best practices for app structure建议使用后缀*_test.js

    编辑:您的正则表达式不起作用,因为它被定义为在末尾捕获"spec.js" || "test.js",或者您的规范文件名以"file.js" 结尾;)请参阅http://regex101.com/r/bE9tV9/1

    还有一件事

    我希望我已经足够清楚了。您可以使用 Angular + Require:angular-requirejs-ready 查看我们项目的入门应用程序结构。它已经使用 Karma 和 Protractor 进行了设置和测试。

    【讨论】:

    • glepretre,非常感谢您提供的全面且非常有帮助的回答——这对我有很大帮助。唯一一件事——我会将你的答案标记为“已接受”——既然我已经成功运行测试,我想了解更多关于test-main.js 中的配置设置的信息。我从 Karma 生成的“allTestFiles”和var TEST_REGEXP = /(spec|test)\.js$/i; 切换到在线提供的var tests = [];if (/Spec\.js$/.test(file)) {...}(请参阅我上面的编辑)。您能否为我们的“非 RegExp”编码人员详细说明这一点?只是想了解为什么这对我来说失败了-谢谢
    • @Cody 当然,您的旧正则表达式被定义为在末尾捕获"spec.js" || "test.js",或者您的规范文件名以"file.js" 结尾;)请参阅regex101.com/r/bE9tV9/1。不客气,如果能帮到你我很高兴!
    • 你绝对帮了我!感谢一吨(吨)。很棒的答案——我会将此标记为已接受的答案——干杯
    猜你喜欢
    • 2016-01-14
    • 2012-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-22
    • 2013-03-25
    • 1970-01-01
    • 2021-04-08
    相关资源
    最近更新 更多