测试是开发过程中重要的部分。尽管 linting 这样的技术可以帮助发现和解决问题,但它们也有其局限性。我们可以在代码和应用程序中启用不同层级的测试。
您可以对特定的代码进行单元测试,也可以从用户的角度来查看应用程序以进行验收测试。集成测试处于两者之间,并关注单独的代码单元如何一起工作。
你可以找到很多 JavaScript 测试工具。只要配置正确,Webpack 中可以使用非常流行的测试工具。尽管这些工具可以在没有 Webpack 的情况下独立工作,但通过 Webpack 运行它们可以让您处理测试工具无法理解的代码语法,同时控制模块的解析方式。您也可以使用Webpack 的监视模式,而不是依赖于测试工具提供的模式。
Mocha
Mocha 是流行的 Node 测试框架。虽然 Mocha 提供了基本的测试工具,但您必须另外提供断言库。Node assert 就已经足够使用了,当然,Mocha 还可以与其他断言库一起使用。
mocha-loader 使您可以在 Webpack 中运行 Mocha 测试。mocha-webpack是另一个旨在提供更多功能的替代方案。
使用 Webpack 配置 mocha-loader
首先,将 Mocha 和 mocha-loader 安装在您的项目中:
npm install mocha mocha-loader --save-dev
设置要测试的代码
我们写一个用来测试的函数:
tests/add.js
module.exports = (a, b) => a + b;
然后,我们为上面的函数写一个测试用例:
tests/add.test.js
const assert = require("assert");
const add = require("./add");
describe("Demo", () => {
it("should add correctly", () => {
assert.equal(add(1, 1), 2);
});
});
配置 Mocha
要用 Mocha 来运行上面的测试,我们添加以下脚本:
package.json
"scripts": {
"test:mocha": "mocha tests",
...
},
好了,现在执行 npm run test:mocha
,您应该可以看到以下输出:
Demo
should add correctly
1 passing (5ms)
Mocha 还提供了监视模式(npm run test:mocha -- --watch
),在您修改代码时自动运行测试li。
--grep <pattern>
可以用于约束测试行为,当你只想测试某些用例的时候可以使用这个选项。
配置 Webpack
通过一些接口,Webpack 也可以提供类似的功能。本书前面已经解决了问题的难点,剩下的就是通过配置将这些解决方案结合起来。
要想让 Webpack 知道要运行哪些测试,需要以某种方式导入它们。动态加载一章讨论的require.context
,是我们可以根据特定的规则聚合文件,这是个不错的工具。设置入口点如下:
tests/index.js
// 在 Node 中跳过执行
if (module.hot) {
const context = require.context(
"mocha-loader!./", // 在处理过程中加入 mocha-loader
false, // 跳过递归查找文件
/\.test.js$/ // 只抓取以 .test.js 结尾的文件
);
// 执行每一个测试用例
context.keys().forEach(context);
}
Webpack 方面需要进行一些小改动:
webpack.mocha.js
const path = require("path");
const merge = require("webpack-merge");
const parts = require("./webpack.parts");
module.exports = merge([
parts.devServer(),
parts.page({
title: "Mocha demo",
entry: {
tests: path.join(__dirname, "tests"),
},
}),
]);
有关
devServer
的完整设置,请参阅“组合 Webpack 配置”一章 。页面设置在“多页”一章中进行了说明。
添加脚本以便于运行:
package.json
"scripts": {
"test:mocha:watch":
"webpack-dev-server --hot --config webpack.mocha.js",
...
},
如果您对
--hot
感到疑惑,请参阅热模块更换附录。
如果您现在运行服务器并导航到 http://localhost:8080/
,您可以看到以下测试结果:
调整测试或代码就会导致浏览器发生变化,您可以在增加用例或重构代码的同时,在浏览器中查看测试的状态。
与直接使用 Mocha 相比,通过 Webpack 配置 Mocha 具有以下几个优点:
- 可以调整模块的解析模式。Webpack 中的技术现在也可以派上用场了,但这也会将代码绑定到 Webpack 中。
- 您可以使用 Webpack 来处理代码的编译工作。如果直接使用 Mocha,我们还要额外地做许多工作。
在缺点方面,现在需要一个浏览器来检查测试。mocha-loader 是最好的开发助手,我们可以通过一个无界面的浏览器来查看测试结果。
karma 和 mocha
Karma 是一个测试工具,允许您在真实设备和无界面浏览器 PhantomJS 上运行测试。karma-webpack 是一个 Karma 预处理器,允许您将Karma 与 Webpack 连接起来。与之前的功能是一样的,然而,这一次,对测试环境有了更多的控制。
我们先安装 Karma、Mocha、karma-mocha 和 karma-webpack:
npm install karma mocha karma-mocha karma-webpack --save-dev
与 Webpack 一样,Karma 也依赖于配置。按如下方式设置文件以使其获取测试:
karma.conf.js
const parts = require("./webpack.parts");
module.exports = config => {
const tests = "tests/*.test.js";
config.set({
frameworks: ["mocha"],
files: [
{
pattern: tests,
},
],
preprocessors: {
[tests]: ["webpack"],
},
webpack: parts.loadJavaScript(),
singleRun: true,
});
};
每次测试都会生成一个包。如果您有大量测试并希望提高性能,请按照上面配置 Mocha 的方式配置
require.context
。有关详细信息,请参阅 karma-webpack issue 23。
添加 npm 快捷方式:
...
"scripts": {
"test:karma": "karma start",
...
},
...
如果你现在执行 npm run test:karma
,你应该看到终端输出:
...
webpack: Compiled successfully.
...:INFO [karma]: Karma v1.7.1 server started at http://0.0.0.0:9876/
以上意味着 Karma 已经就绪,您必须访问该 URL 才能运行测试。如果配置中设置了singleRun: true
,在测试结束后 Karma 就会以 0 或 1 退出:
...
...:INFO [karma]: Karma v1.7.1 server started at http://0.0.0.0:9876/
...:INFO [Chrome 61...]: Connected on socket D...A with id manual-73
Chrome 61...): Executed 1 of 1 SUCCESS (0.003 secs / 0 secs)
鉴于运行测试这种方式会变得烦人,配置替代方法是个好主意。使用 PhantomJS 是一种选择。
您可以通过
browsers
字段将 Karma 指向特定的浏览器。例如:browsers: ['Chrome']
。
通过 PhantomJS 运行测试
通过 PhantomJS 运行测试需要几个依赖项:
npm install karma-phantomjs-launcher phantomjs-prebuilt --save-dev
要通过 Phantom 进行 Karma 测试,请按如下方式调整其配置:
karma.conf.js
module.exports = config => {
...
config.set({
...
browsers: ["PhantomJS"],
});
};
如果再次运行测试(npm run test:karma
),则无需访问 URL 即可获得输出:
...
webpack: Compiled successfully.
...:INFO [karma]: Karma v1.7.1 server started at http://0.0.0.0:9876/
...:INFO [launcher]: Launching browser PhantomJS with unlimited concurrency
...:INFO [launcher]: Starting browser PhantomJS
...:INFO [PhantomJS ...]: Connected on socket 7...A with id 123
PhantomJS ...: Executed 1 of 1 SUCCESS (0.005 secs / 0.001 secs)
每次改动都要手动运行测试非常的麻烦,Karma 提供了一种监视模式。
PhantomJS 尚不支持 ES2015 功能,因此您必须预处理测试代码。PhantomJS 2.5 将提供 ES2015 支持。
使用 Karma 监视模式
可以按如下方式访问Karma的监视模式:
package.json
"scripts": {
"test:karma:watch": "karma start --auto-watch --no-single-run",
...
},
如果您现在执行 npm run test:karma:watch
,文件改动后测试就自动运行了。
生成覆盖率报告
要想了解代码的测试覆盖情况,最好生成覆盖率报告。这样做需要代码级检测。此外,必须要有报告信息,并且可以通过 HTML 和 LCOV 的格式输出报告。
LCOV 与可视化服务完美集成。您可以通过持续集成环境将覆盖信息发送到外部服务,这样就可以随时跟踪代码状态。
isparta 是一种流行的兼容 ES2015 语法的代码覆盖工具。将其与Karma 连接需要进行一些配置。最重要的是,代码必须通过 babel-plugin-istanbul 进行检测。这样做需要少量的 Webpack 配置。另外,我们还需要使用 karma-coverage 来解决报告问题。
首先安装依赖项:
npm install babel-plugin-istanbul karma-coverage --save-dev
连接 Babel 插件,以便在运行 Karma 时进行检测:
.babelrc
...
"env": {
"karma": {
"plugins": [
[
"istanbul",
{ "exclude": ["tests/*.test.js"] }
]
]
}
}
设置 Babel 环境变量:
karma.conf.js
module.exports = config => {
...
process.env.BABEL_ENV = "karma";
config.set({
...
});
};
如果您对
env
感到疑惑,请参阅加载 JavaScript 章节。
对于 Karma,我们必须配置覆盖报告,并且必须将 Karma 配置与 Webpack 连接起来。 karma-webpack 为此提供了两个字段:webpack
和 webpackMiddleware
,您必须使用前者,以让 Babel 对代码进行处理。
karma.conf.js
const path = require("path");
...
module.exports = config => {
...
config.set({
...
reporters: ["coverage"],
coverageReporter: {
dir: "build",
reporters: [{ type: "html" }, { type: "lcov" }],
},
});
};
如果要将报告发送到
dir
下特定的目录,请为每个报告设置subdir
。
如果你现在运行测试(npm run test:karma
),你应该在构建下面看到一个包含覆盖率报告的新目录。可以通过浏览器查看 HTML 报告。
LCOV 需要特定的工具才能工作。您可以找到编辑器插件,例如 Atom 的 lcov-info。在使用监视模式进行开发时,正确配置的插件可以为您提供覆盖信息。
Jest
Facebook 的 Jest 是另外一种测试工具,非常的轻量,封装一些基本功能,包括覆盖和数据模拟。它可以捕获数据的快照,当你需要记录或保存相关数据时,这会比较有价值。
Jest 测试遵循 Jasmine 测试框架语义,它支持开箱即用的 Jasmine 风格的断言。特别是测试用例的定义与 Mocha 非常相似,因此当前测试应该可以在不对测试代码本身进行任何调整的情况下工作。Jest 提供了 jest-codemods 工具用于将更复杂的项目迁移到 Jest 中。
首先安装 Jest:
npm install jest --save-dev
Jest 通过 package.json 配置捕获测试。它检测 tests 目录中的测试用例:
package.json
"scripts": {
"test:jest:watch": "jest --watch",
"test:jest": "jest",
...
},
现在您有两个新命令:一个在普通模式下运行测试,另一个用于在监视模式下运行它们。要捕获覆盖率信息,您必须在 package.json 中针对 jest
设置 "collectCoverage": true
或将 --coverage
标志传递给 Jest。默认情况下,覆盖报告会保存在 coverage 目录下。
鉴于生成覆盖率报告带来性能开销,通过标志启用可能是一个好主意。这样您就可以准确控制何时生成报告了。
将 Webpack 设置移植到 Jest 需要更多的努力,特别是如果您依赖 Webpack 特定功能。官方指南涵盖了一些常见问题。您还可以配置 Jest 以使用 Babel,通过 babel-jest 会比较方便,因为它允许您使用像 babel-plugin-module-resolver 这样的 Babel 插件来匹配 Webpack 的功能。
jest-webpack 集成了 Webpack 和 Jest。
AVA
AVA是一个可以并行运行的测试工具。它附带了自己的测试用例定义。webpack-ava-recipe 介绍了如何将其与 Webpack 连接。
两者结合的关键在于,在监视模式下运行 Webpack 和 AVA,以使 Webpack 对代码进行处理,同时允许 AVA 使用已处理的代码。通过 require.context
使用 Mocha 的方法在这里也可以派上用场,因为您必须捕获测试文件以便 Webpack 以某种方式处理。
Mock 数据
数据模拟是一种允许您替换测试对象的技术。考虑以下解决方案:
- sinon 提供 Mock、Stub 和 Spy 等功能。从2.0 版开始,它可以和 Webpack 配合使用。
- inject-loader 允许您通过依赖项将代码注入模块,这对于 Mock 来说是有价值的。
- rewire-webpack 允许 Mock 和覆盖模块全局变量。babel-plugin-rewire 为 Babel 实现了 rewire。
从测试中删除文件
如果您通过 Webpack 执行测试,您可能希望改变它处理类似图像这样的资源的方式。您可以匹配这些资源,然后使用 noop
函数替换模块,如下所示:
plugins: [
new webpack.NormalModuleReplacementPlugin(
/\.(gif|png|scss|css)$/, "lodash/noop"
),
]
总结
Webpack 可以配置为使用各种各样的测试工具。每个工具都有它的独特优势,但它们也有相当多的共同点。
回顾一下:
- 通过 Webpack 运行测试工具可以让您从 Webpack 的模块解析机制中受益。
- 有时,测试设置可能非常复杂。像 Jest 这样的工具可以使您以最少的配置开发测试。
- 您可以为 Webpack 找到多个数据模拟工具。它们允许您塑造测试环境,但有时通过合理的设计你也可以避免数据模拟。
您将在下一章中学习使用 Webpack 部署应用程序。