Mocha是一个功能丰富的javascript测试框架,运行在node.js和浏览器中,使异步测试变得简单有趣。为Javascript应用程序添加测试,不仅可以保证代码的质量,还可以获得一个像这样的小徽章 。除了Mocha,Jest、Ava 和 Jasmine等都是不错的选择。
之前写过的一个小项目,就是使用Mocha测试的,可以当作实战参考。
如果你还在纠结,怎么使用Webpack在本地创建一个项目,可以参考上一篇文章webpack入门。
一、安装
1.1、安装Mocha
# 作为项目的依赖安装
npm install --save-dev mocha
# 全局安装
npm install --global mocha
1.2、安装chai
chai是用于node和浏览器的BDD/TDD断言库,可以和任何测试框架完美搭配。
npm install chai
1.3、配置package.json
{
"scripts": {
"test": " mocha --exit"
}
}
二、编写测试脚本
首先,需要一个被测试的模块 src/sum.js,内容如下
function sum (a, b) {
return a + b
}
module.exports = sum
要检测sum模块的功能是否符合我们的预期,需要编写一个测试脚本进 test/index.js进行验证。
const sum = require(''../src/sum.js)
const assert = require('assert')
const assert = require('assert')
describe('Sum test', function () {
it('test 1 + 9', function (done) {
assert.equal(sum, 9)
})
})
- describe方法用于描述测试套件(test suit)的结构。describe下需要包含至少一个it或describe声明。
- it用于标识每个单独的测试,但it并不会告诉mocha关于测试套件的组成。
- chai有三种断言风格:expect、assert和should。你可以选择自己喜欢的一种。
- assert.equal(actual, expected, [message])
- @param { Mixed } actual 实际的结果
- @param { Mixed } expected 期望的结果
- @param { String } message 可选参数,消息
断言 actual == expected,如果actual不等于expected,则会在测试过程中抛出一个错误Error: Uncaught [AssertionError: 9 == 10]
然后运行测试
npm run test
2.1、异步代码
向it() 回调中添加一个形参(通常是 done ),mocha就会等待done被调用,然后才结束测试。done会接受一个Error实例或一个false值,使用其他的任何值都是无效的,并且会抛出一个错误(通常会导致测试失败)。describe('User', function() { describe('#save()', function() { it('should save without error', function(done) { var user = new User('Luna'); user.save(function(err) { if (err) done(err); else done(); }); }); }); });
2.1.1、使用async和await
如果你的环境支持async和await,你也可以像这样编写一个异步测试beforeEach(async function() { await db.clear(); await db.save([tobi, loki, jane]); }); describe('#find()', function() { it('responds with matching records', async function() { const users = await db.find({type: 'User'}); users.should.have.length(3); }); });
2.1.2、使用Promise
你也可以return一个Promise来替代done的功能。
在v3.3.0版本或更高的版本中,如果你同时使用了promise和done,将会导致异常。const assert = require('assert'); // antipattern it('should complete this test', function(done) { return new Promise(function(resolve) { assert.ok(true); resolve(); }); });
三、配置mocha
在项目的根目录中创建一个文件 .mocharc.json{ "require": ["@babel/register"], "file": null, "watch": true }
- file:array|string,默认是Null。 明确指定在加载测试文件之前,加载的文件。例如,你想在其他所有测试文件的每次测试之前,运行钩子函数时,非常有用 }
- require:array,默认是null。在加载用户界面或是测试文件之前,require一个模块。mocha默认是不支持ES6语法的,如果你使用ES6或更高的语法编写测试用例,你需要安装babel,然后修改require属性。
- watch:boolean。监听当前文件夹下的文件,如果有文件被修改,就会重新运行测试
- watch-files:array。指定监听的文件。
- watch-ingore:array,默认["node_modules",".git"]。不被监听的路径。
- reporter:指定测试报告的格式,默认是 "spec" ,可选值: "tap" 、 "doc" 。
这些配置也可以在package.json中指定{ "scripts": { "test": "mocha --require @babel/register --watch" } }
四、Hooks
Mocha提供了 before() , after(), beforeEach() 和 afterEach() 钩子函数。这些函数用于设置准备测试的前提条件,并在测试后进行清理工作。describe('hooks', function() { before(function() { // 在此作用域的第一个测试之前运行一次 }); after(function() { // 在此作用域的最后一个测试之后运行一次 }); beforeEach(function() { // 在此作用域的每个测试运行之前运行 }); // 你也可以为钩子函数定义一个名称 afterEach('Custom name', function() { // 在此作用域的每个测试运行之之后运行 }); // test cases it('test', function () { // test }) });
4.1、异步hooks
before() , after(), beforeEach() 和 afterEach() 钩子函数也可以同步或异步执行。例如,在每个测试开始之前,你需要使用虚拟数据填充数据库。
describe('Connection', function() {
var db = new Connection(),
tobi = new User('tobi'),
loki = new User('loki'),
jane = new User('jane');
beforeEach(function(done) {
db.clear(function(err) {
if (err) return done(err);
db.save([tobi, loki, jane], done);
});
});
describe('#find()', function() {
it('respond with matching records', function(done) {
db.find({type: 'User'}, function(err, res) {
if (err) return done(err);
res.should.have.length(3);
done();
});
});
});
});
在上面的例子中,beforeEach指定了一个形参done,mocha就会等待done被调用,然后开始执行测试用例。
五、chai的assert断言风格
chai.***(actual, expected, [message])
- @param { Mixed } actual 期望值
- @param { Mixed } expected 实际获得的值
- @param { String } message 可选,消息
断言 actual == expected
const assert = require('assert') // 断言 actual == expected assert.equal(3, '3', '== coerces values to strings'); // 断言 actual != expected assert.notEqual(3, 4, 'these numbers are not equal'); // 断言actual === expected assert.strictEqual(true, true); // 断言actual !== expected assert.notStrictEqual(3, '3'); // 深度比较两个对象 assert.deepEqual({ tea: 'green' }, { tea: 'green' }); // 深度比较两个对象 assert.notDeepEqual({ tea: 'green' }, { tea: 'jasmine' }); // 判断值是否是true assert.isTrue(teaServed, 'the tea has been served'); // 判断值不是true assert.isNotTrue(teaServed, 'the tea has been served'); // 判断值是一个函数 function serveTea() { return 'cup of tea'; }; assert.isFunction(serveTea, 'great, we can have tea now'); // isBoolean isNotBoolean isNotNumber isNumber isNotString isString isNotArray isArray isObject isNotObject同上 //---------------------------------------------- // include查询元素的时候,使用的是=== assert.include([1,2,3], 2); assert.include('foobar', 'foo'); assert.include({ foo: 'bar', hello: 'universe' }, { foo: 'bar' }, 'object contains property'); // 断言值匹配正则表达式 assert.match('foobar', /^foo/'); // 断言值不匹配正则表达式 assert.notMatch('foobar', /^foo/, 'regexp does not match');
六、使用JSDOM
Node环境是没有window对象和dom对象的,如果你要测试的项目中需要和DOM交互,你就会需要JSDOM了。
然后创建一个文件test/generate-page.jsnpm install --save-dev --save-exact jsdom jsdom-global
然后修改 .mocharc.jsonconst jsdom = require('jsdom') const { JSDOM } = jsdom const template = `<!DOCTYPE html> <html font-size="62.5%" lang="zh"> <head> </head> <body> <div>blog1997</div> <div class="canvas"> <a><img class="lazy" data-src=""/></a> <a><img class="lazy" data-src=""/></a> <a><img class="lazy" data-src=""/></a> <ul> <li>1</li> <li>2</li> <li>3</li> </ul> </div> </body> </html>` global.jsdom = new JSDOM(template, { resources: 'usable', // 允许执行script标签中的脚本,只有配合 runScripts: "dangerously"才可用 pretendToBeVisual: true, // 生成window.cancelAnimationFrame()和window.requestAnimationFrame()方法 runScripts: "dangerously" }) global.window = global.jsdom.window global.document = global.jsdom.window.document global.div = global.document.querySelector('div')
这样,你的测试用例就可以和DOM交互了{ "require": ["@babel/register", "jsdom-global/register", "./test/generate-page.js"] }
describe('easing test', function () { it('test swing', function (done) { Animate(document.querySelector('div'), { width: '126px', easing: 'swing' }, (el) => { assert.equal(window.getComputedStyle(el).width, '126px') done() }) }) }
七、生成测试覆盖率报告
首先安装nyc
配置package.jsonnpm i nyc --save-dev
{ "scripts": { "test": " mocha", "coverage": "cross-env NODE_ENV=test nyc --reporter=lcov npm run test" } }
- --reporter:配置生成测试覆盖率报告的格式,可以指定多个
然后运行命令
之后就会在项目根目录中创建一个文件夹coverage,里面存放了测试覆盖率的内容npm run coverage
八、生成测试覆盖率报告的徽章
要生成一个测试覆盖率的徽章,首先,你需要将你的代码上传到GIT上,然后通过codecov平台(国内访问有时候可能比较慢),获取一个徽章。
如图所示,点击add new repository,将你的项目添加到codecov平台上。然后我们会得到一个像这样的token
CODECOV_TOKEN='1f721be7-4609-4cae-1433-3a84d1d8b2df'
8.1、安装codecov
然后添加codecov的配置文件codecov.ymlnpm install -g codecov
修改package.jsonparsers: javascript: enable_partials: yes coverage: range: 70..100 round: down precision: 2 codecov: token: 你的token
运行命令{ "scripts": { "test": " mocha", "coverage": "cross-env NODE_ENV=test nyc --reporter=lcov npm run test", "report-coverage": "codecov" } }
然后测试覆盖率就上传到codecov中去了,点击setting > badge。就可以获取小徽章了npm run coverage npm run report-coverage
暂无评论