什麼是 karma
karma 是一個提升測試效率的工具,幫助開發者更好更快速地在多種環境下執行測試代碼,拿到測試結果。在運作的時候,它會自動啟動配置好的浏覽器,同時也會啟動一個 node 伺服器,然後在啟動好的浏覽器中執行測試代碼,并将測試代碼執行結果傳回給 node 伺服器,然後 node 伺服器在列印出收到的執行結果。
安裝 karma
可以通過 npm 安裝 karma :
// 本地安裝
npm i karma --save-dev
// 全局安裝
npm i karma -g
初始化 karma
安裝完成之後,切換到目标項目根目錄,運作:
karma init
這樣就會以向導的形式生成 karma 的配置檔案 karma.conf.js ,檔案内容大緻為:
// Karma configuration
// Generated on Wed Jun 29 2016 23:22:24 GMT+0800 (CST)
module.exports = function(config) {
config.set({
// 根路徑,後面配置的基本所有相對路徑都會根據這個路徑來構造。
basePath: '',
// 使用到的架構
// 目前支援的架構: https://npmjs.org/browse/keyword/karma-adapter
frameworks: ['jasmine', 'requirejs'],
// 将會在浏覽器裡面執行的代碼
files: [
'test/main.js',
{
pattern: 'src/**/*.js',
// false 表示初始化的時候不會使用 script 标簽直接将相關 js 引入到浏覽器,需要自己寫代碼加載
included: false
},
{
pattern: 'test/**/*Spec.js',
included: false
}
],
// 需要從 files 中排除掉的檔案
exclude: [],
// 需要做預處理的檔案,以及這些檔案對應的預處理器。
// 此處就可以将 coffee 、 ES6 等代碼轉換一下。
preprocessors: {
'src/**/*.js': ['babel', 'coverage'],
'test/**/!(main).js': ['babel', 'coverage'],
'node_modules/protectobject/src/**/*.js': ['babel']
},
// babel 預處理器的配置
babelPreprocessor: {
options: {
presets: ['es2015', 'stage-0'],
plugins: ['transform-decorators-legacy', 'transform-es2015-modules-amd']
}
},
// 覆寫率報告器配置
coverageReporter: {
type: 'html',
dir: 'coverage'
},
// 實際使用的報告期
// 可用的報告器: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['dots', 'coverage'],
// 伺服器端口号
port: ,
// 在輸出内容(報告器和日志)中啟用/禁用顔色
colors: true,
// 日志級别
// 取值: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,
// 啟用/禁用監視檔案變化重新執行測試的功能
autoWatch: true,
// 要測試的目标環境
browsers: ['Chrome', 'Firefox', 'Safari'],
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
singleRun: false,
// Concurrency level
// how many browser should be started simultaneous
concurrency: Infinity
});
};
配置 Require.js
對于 Require.js ,還要注意配置一個入口檔案,主要用于配置 Require.js 的子產品資訊等。
上述 karma 配置檔案中的 test/main.js 即為 Require.js 的入口檔案,在該檔案中的代碼一般來說應該是這樣的:
var TEST_REGEXP = /(spec|test)/.js$/i;
var allTestFiles = [];
// Get a list of all the test files to include
Object.keys(window.__karma__.files).forEach(function(file) {
if (TEST_REGEXP.test(file)) {
// Normalize paths to RequireJS module names.
// If you require sub-dependencies of test files to be loaded as-is (requiring file extension)
// then do not normalize the paths
var normalizedTestModule = file.replace(/^//base//|/.js$/g, '');
allTestFiles.push(normalizedTestModule);
}
});
require.config({
// Karma serves files under /base, which is the basePath from your config file
baseUrl: '/base/src',
// example of using a couple of path translations (paths), to allow us to refer to different library dependencies, without using relative paths
paths: {
'jquery': '../lib/jquery',
'underscore': '../lib/underscore',
},
// example of using a shim, to load non AMD libraries (such as underscore)
shim: {
'underscore': {
exports: '_'
}
},
// dynamically load all test files
deps: allTestFiles,
// we have to kickoff jasmine, as it is asynchronous
callback: window.__karma__.start
});
執行測試
運作如下指令,執行測試:
karma start
karma 分析
在執行測試的時候,點選 debug 按鈕,進入 debug 頁面,然後打開浏覽器開發者工具,可以看到在 HTML 中有一段 js 代碼:
// Configure our Karma
window.__karma__.config = {"args":[],"useIframe":true,"captureConsole":true,"clearContext":true};
// All served files with the latest timestamps
window.__karma__.files = {
'/base/node_modules/requirejs/require.js': '2c8b45573db27c131094a113e995236d20f043bb',
'/base/node_modules/karma-requirejs/lib/adapter.js': '2621a4400d4a8a49588243fce2d8609ef950b46a',
'/base/node_modules/jasmine-core/lib/jasmine-core/jasmine.js': '391e45351df9ee35392d2e5cb623221a969fc009',
'/base/node_modules/karma-jasmine/lib/boot.js': '945a38bf4e45ad2770eb94868231905a04a0bd3e',
'/base/node_modules/karma-jasmine/lib/adapter.js': '7975a273517f1eb29d7bd018790fd4c7b9a485d5',
'/base/test/main.js': 'fc5206f4dff3b583db818cb10ed7c5cade572896',
'/base/src/State.js': 'db89a58b4570983b8f8febfd4dedbc586c353670',
'/base/test/StateSpec.js': 'faf31b373690a6d7a7035fdfdc9c85d906ace5c1'
};
可以看到 window.__karma__.files 中列出了所有的可能會在浏覽器中執行的 js ,如果通過 Require.js 加載這裡沒有列舉出來的 js ,就會報錯。
然後再看下面的一堆 script 标簽,大緻是這樣的:
<script type="text/javascript" src="/base/node_modules/requirejs/require.js"></script>
<script type="text/javascript" src="/base/node_modules/karma-requirejs/lib/adapter.js"></script>
<script type="text/javascript" src="/base/node_modules/jasmine-core/lib/jasmine-core/jasmine.js"></script>
<script type="text/javascript" src="/base/node_modules/karma-jasmine/lib/boot.js"></script>
<script type="text/javascript" src="/base/node_modules/karma-jasmine/lib/adapter.js"></script>
<script type="text/javascript" src="/base/test/main.js"></script>
<script type="text/javascript">
window.__karma__.loaded();
</script>
可以看到,直接引入了 require.js 、 karma 相關的一堆 js 、jasmine 相關的 js ,還直接引入了剛才配置的 test/main.js (Require.js 入口檔案)。注意,此處并沒有直接引入 included: false 的 js 。
URL 路徑中的 base
如果仔細看各種資源請求的 URL 位址,會發現除了 debug.js 和 context.js 之外,其它 js 檔案都會以 /base 開頭,在配置 Require.js 的時候,務必注意這一點。
coverage
可以引入 karma 的 coverage 插件來檢視測試覆寫率,該插件會在目标代碼中插入很多額外的代碼,用于判斷測試代碼執行流程有沒有走到這些地方。在 debug 的時候,最好關掉 coverage 功能,不然這些額外的代碼非常影響調試。
另外 karma-coverage-es6 聲稱支援 ES6 ,但是似乎并不行?
jasmine 的 HTML reporter
預設情況下,浏覽器中 debug 頁面是不會輸出任何 jamine 測試結果的,可以借助 karma-jasmine-html-reporter 解決這個問題。
但是, karma-jasmine-html-reporter 有坑,在該插件的 index.js 中,有這樣一段代碼:
var createPattern = function(path) {
return {pattern: path, included: true, served: true, watched: false};
};
var initReporter = function(files, baseReporterDecorator) {
baseReporterDecorator(this);
files.unshift(createPattern(__dirname + '/lib/adapter.js'));
files.unshift(createPattern(__dirname + '/lib/html.jasmine.reporter.js'));
files.unshift(createPattern(__dirname + '/css/jasmine.css'));
};
initReporter.$inject = ['config.files', 'baseReporterDecorator'];
module.exports = {
'reporter:kjhtml': ['type', initReporter]
};
files 指的就是 karma.conf.js 中配置的 files ,此處使用 unshift 方法将這堆 js 、 css 放在了 files 最前面,這樣就會導緻 html.jasmine.reporter.js 先于 jasmine.js 加載,進而報錯( html.jasmine.reporter.js 是要依賴 jasmine.js 的),是以這裡最好根據項目的實際情況,合理調整一下順序。