ㅤㅤㅤ
ㅤㅤㅤ
ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ(不应当急于求成,应当去熟悉自己的研究对象,锲而不舍,时间会成全一切。凡事开始最难,然而更难的是何以善终。——莎士比亚)
ㅤㅤㅤ
ㅤㅤㅤ
ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ
指对软件中的最小可测试单元进行检查和验证。对于单元测试中单元的含义,一般来说,要根据实际情况去判定其具体含义,如C语言中单元指一个函数,Java里单元指一个类,图形化的软件中可以指一个窗口或一个菜单等。总的来说,单元就是人为规定的最小的被测功能模块。单元测试是在软件开发过程中要进行的最低级别的测试活动,软件的独立单元将在与程序的其他部分相隔离的情况下进行测试
- 保障软件项目质量,减少企业损失
没有经过测试的代码犹如在战场上裸奔的士兵。人不是机器,即便是测试人员,也不能保证每个功能是否完全稳定,因为只有先让开发者先去消费它,才能给予最重要的质量保证
- 减少企业测试成本,避免投入冗余的测试资源
高质量的单元测试远远比人工测试更高效。在’全自动化’,'人工智能’的大时代,机器远远比人工更高效,与其投入大量的测试资源,比如花时间编写高效稳定的单元测试
- 更精准的定位程序中的bug
测试基于表面去判断问题,而只有开发是知其所以然的。毕竟代码是开发者编写的,测试人员的优势只在于时间和对功能的熟悉度,若有了单元测试,不仅能节省测试人员的时间,还能够减少程序bug数量
- 避免开发者对测试人员产生依赖,完全不关心其代码的测试
人都是有惰性的,因为 测试人员的存在,导致开发者对自己编写的代码质量漠不关心。对测试人员过度依赖的项目必定是不稳定和不安全的
- 更高的可维护性
由于缺少单元测试,程序中存在着’坏代码’。一些’千年老bug’,'临时拆东墙补西墙的解决方案’使得代码的维护和重构难度愈来愈大,牵一发而动全身。通过单元测试尽可能的提高代码覆盖率
- 了解你的代码性能
虽然这不是单元测试的主要目的,但在单元测试中编写运行指定场景的性能分析会非常容易,在现有的代码测试中快速复制一份能让指定对象运行起来的测试代码,运行它测量性能,修改代码查看性能的变化情况,像德芙一样丝滑
- 证明你的工作已完成
无论需求驱动还是测试驱动,你的任务通常都是完成一系列代码,实现具体的一系列功能。那么当你提交代码时,你怎么证明你的提交时这个功能中的未完成部分还是已经全部完成,如何证明你功能是否经过测试可以随时合并到生产环境,换句话说,你怎么证明你的代码完全按照实现的“约定行为”运行的。
当我们仅使用口头或者书面形式描述“约定行为”时,非常困难的一点是如何消除自然语言中的歧义,如何避免不同的人们基于各自不同背景知识脑补出不同的内容导致隐形的分歧。而使用代码来描述行为规约的一个好处就是计算机运行需要指定的指令,不会存在二义性。因此用一段运行的代码可以完整无误的证明你的代码是完成的
如果一段代码承担的职责越多,为其编写单元的时候就要构建更多的输入数据,然后推测它的输入。比如,一段代码中既包含数据库的链接,也包含查询,那么为它编写测试用例就要同事关注数据库连接和数据库查询。较好的方式是将这两种职责进行解耦分离,变成两个单一职责的方法,分别测试数据库连接和数据库查询
通过对程序代码进行接口抽象后,我们可以针对接口进行测试,而具体代码实现的变化不影响为接口编写的单元测试
层次分离实际上是单一职责的一种实现。在MVC结构的应用中,就是典型的层次分离模型,如果不分离各个层次,无法想象这个代码该如何切入测试。通过分层之后,可以逐层测试,逐层保证
对于开发者来说,不仅要编写单元测试,还应当编写可测试代码
BDD和TDD区别
测试驱动开发是敏捷开发中的一项核心实践和技术,也是一种设计方法论。TDD的原理是在开发功能代码之前,先编写单元测试用例代码,测试代码确定需要编写什么产品代码。TDD的基本思路就是通过测试来推动整个开发的进行,但测试驱动开发并不只是单纯的测试工作,而是把需求分析,设计,质量控制量化的过程。TDD首先考虑使用需求(对象、功能、过程、接口等),主要是编写测试用例框架对功能的过程和接口进行设计,而测试框架可以持续进行验证
var assert = require('assert');
var mocha = require('mocha');
var suite = mocha.suite;
var setup = mocha.setup;
var suiteSetup = mocha.suiteSetup;
var test = mocha.test;
var teardown = mocha.teardown;
var suiteTeardown = mocha.suiteTeardown;
//test case
suite('Array', function() {
suiteSetup(function() {
//suiteSetup will run only 1 time in suite Array, before all suite
//...
console.log('suitSetup...');
});
setup(function() {
//setup will run 1 time before every suite runs in suite Array
//...
console.log('setup...');
});
suite('indexOf()', function() {
test('should return -1 when not present', function() {
assert.equal(-1, [1, 2, 3].indexOf(4));
});
});
suite('indexOf2()', function() {
test('should return not -1 when present', function() {
assert.equal(0, [1, 2, 3].indexOf(1));
});
});
teardown(function() {
//teardown will run 1 time after every suite runs in suite Array
//...
console.log('teardown...');
});
suiteTeardown(function() {
//suiteTeardown will run 1 time in suite Array, after all suits run over.
//...
console.log('suiteTeardown...');
});
});
- suite:定义一组测试用例。
- suiteSetup:此方法会在这个 suite 所有测试用例执行前执行一次,只一次,这是跟 setup 的区别。
- setup:此方法会在每个测试用例执行前都执行一遍。
- test:具体执行的测试用例实现代码。
- teardown:此方法会在每个测试用例执行后都执行一遍,与 setup 相反。
- suiteTeardown:此方法会在这个 suite 所有测试用例执行后执行一次,与 suiteSetup 相反
行为驱动开发是一种敏捷软件开发的技术,它鼓励软件项目中的开发者、QA和非技术人员或商业参与者之间的协作。主要是从用户的需求出发,强调系统行为。BDD最初是由Dan North在2003年命名,它包括验收测试和客户测试驱动等的极限编程的实践,作为对测试驱动开发的回应。
describe('Array', function() {
before(function() {
// ...
});
describe('#indexOf()', function() {
it('should return -1 when not present', function() {
[1, 2, 3].indexOf(4).should.equal(-1);
});
});
after(function() {
// ...
});
});
- describe():描述场景,在里面可以设定 Context,可包括多个测试用例,也可以嵌套场景
- it():位于场景内,描述测试用例
- before():所有测试用例的统一前置动作
- after():所有测试用例的统一后置动作
- beforeEach():每个测试用例的前置动作
- afterEach():每个测试用例的后置动作
在接下来的演示中,我们均采用BDD模式
BDD是一种敏捷软件开发的技术。它对TDD的理念进行了扩展,在TDD中侧重点偏向开发,通过测试用例来规范约束开发者编写出质量更高、bug更少的代码。而BDD更加侧重设计,其要求在设计测试用例的时候对系统进行定义,倡导使用通用的语言将系统的行为描述出来,将系统设计和测试用例结合起来,从而以此为驱动进行开发工作。
BDD的通用语言是一种近乎自然语言的描述软件的形式。传统的开发模式中,客户很难从技术层面理解问题,开发人员很难从业务需求考虑问题,基于这种通用语言形式可以尽可能的避免客户和开发者在沟通上的障碍,实现客户和开发者同时定义系统的需求。避免了因为理解需求不充分而带来的不必必要的工作量。
node中绝大多数单元测试的基础,它可以测试一个条件,如果条件未满足,则抛出错误,很多第三方框架都用了assert模块,但缺点是,一旦检查失败,将会抛出异常停止整个应用,这对于大规模断言检查时,并不友好,因为更通用的做法是,记录抛出的异常,并继续执行,生成测试报告
特性
- 简单
- 轻量
- 原生
var assert = require('assert');
function add (a, b) {
return a + b;
}
var expect = add(1, 2);
assert(expect === 3, 'should return 3');
它类似BDD的风格表示断言,让测试更容易看懂,它的设计之初就是搭配其他测试框架一起使用。
使用上也非常容易,因为它只是给Object.protptype增加了一个should属性,我们可以用它写出表达能力更强的断言
特性
- 更强的语义化
- 不依赖框架
- 基于assert的扩展,轻量,简单
var should = require('should');
var user = {
name: 'tj'
, pets: ['tobi', 'loki', 'jane', 'bandit']
};
user.should.have.property('name', 'tj');
user.should.have.property('pets').with.lengthOf(4);
// If the object was created with Object.create(null)
// then it doesn't inherit `Object.prototype`, so it will not have `.should` getter
// so you can do:
should(user).have.property('name', 'tj');
// also you can test in that way for null's
should(null).not.be.ok();
someAsyncTask(foo, function(err, result){
should.not.exist(err);
should.exist(result);
result.bar.should.equal(foo);
});
当前JavaScript流行的断言库,内置了should,expect,assert,同样的,也是配合框架使用,在接下来的演示中,均采用此断言库演示
特性
- 内置了should,expect,assert
- 插件机制
- 更灵活更语义化的断言
var expect = require('chai').expect
, foo = 'bar'
, beverages = { tea: [ 'chai', 'matcha', 'oolong' ] };
expect(foo).to.be.a('string');
expect(foo).to.equal('bar');
expect(foo).to.have.lengthOf(3);
expect(beverages).to.have.property('tea').with.lengthOf(3);
相对比较新,运行在node.js上的JavaScript测试框架,可以用来做 TDD(Test-Driven Development)或 BDD(Behavior Driven Development)风格的测试,在接下来的演示中,均采用此框架进行演示
特性
- 全局变量泄露测试
Mocha 能够发现那种不小心创建出来的全局变量泄露,如果创建了全局变量,它会在测试时抛出错误。如果想禁用全局泄露检查,可以运行 mocha 命令时加上 --ignored-leaks 选项- 用 Mocha挂钩定义设置和清理逻辑
BDD 接口 beforeEach()、afterEach()、before()和 after()接受回调,可以用来定义设置和清理逻辑。- 测试异步逻辑
给定义测试逻辑函数添加一个参数,就可以把 Mocha 测试用例定义为异步的。这个参数通常被命名为done。
var assert = require('assert');
describe('Array', function() {
describe('#indexOf()', function() {
it('should return -1 when the value is not present', function() {
assert.equal([1, 2, 3].indexOf(4), -1);
});
});
});
一个结构化更强的,且更容易理解的和维护的测试框架,拥有自己的BDD术语定义,和mocha的不同主要体现在风格和并行性上
特性
- 一个测试套件中包含一个或多个批次
- 批次和上下文是并行运行的。上下文中可能包含主题、一个或多个誓约,以及/或者一或多个先关联的情境(内部情况也是并行运行的)
- 主题是跟情境相关的测试逻辑。誓约是对主题结果的测试
var vows = require('vows'),
assert = require('assert');
vows.describe('Deep Thought').addBatch({
'An instance of DeepThought': {
topic: new DeepThought,
'should know the answer to the ultimate question of life': function (deepThought) {
assert.equal (deepThought.question('what is the answer to the universe?'), 42);
}
}
});
ㅤㅤㅤ
ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ
ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ
ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ
有趣 简单 灵活
mocha是一个功能丰富的JavaScript测试框架,可在Node.js和浏览器中运行,从而使异步测试变得简单而有趣。Mocha测试按顺序运行,从而可以灵活,准确地报告,同时将未捕获的异常映射到正确的测试用例
https://github.com/mochajs/mocha/blob/master/example/config/.mocharc.js
我当前mocha的版本是7.1.2,chai的版本是4.2.0
npm install --global mocha
或者npm install --save-dev mocha
编写test.js文件
const assert = require('assert');
describe('Array', function() {
describe('#indexOf()', function() {
it('should return -1 when the value is not present', function() {
assert.equal([1, 2, 3].indexOf(4), -1);
});
});
});
- describe 代表着一组测试,第一个参数是该测试套件的名称,第二个参数是执行函数,且测试用例可以进行嵌套,更方便的进行分层级的测试
- it 代表一个测试用例,是测试脚本的最小单元,第一个参数是该测试用例的名称,第二个参数是执行函数
- context describe的别名,作用相同,一个更语义化的名称,在接下来的演示中,均采用此名称作为测试套件名称
- specify it的别名,和context同理
执行命令mocha test.js得到结果
$ mocha test.js
Array
#indexOf()
✓ should return -1 when the value is not present
1 passing (9ms)
mocha内置了assert断言库,但如果抛出异常,则会停止程序执行,建议使用其他断言库,在这里我们选择搭配chai
- before
在所有测试用例执行前执行
- beforeEach
在每一个测试用例执行前执行
- after
在所有测试用例执行结束后执行
- afterEach
在每一个测试用例结束后执行
import { expect } from 'chai';
context('test sum demo', () => {
before(() => {
console.log('所有测试用例执行前执行');
});
beforeEach(() => {
console.info('每一个测试用例执行前执行');
});
afterEach(() => {
console.info('每一个测试用例执行后运行');
});
after(() => {
console.info(`所有测试用例执行后执行`);
});
specify('test mocha life cycle', () => {
expect(3).to.be.eq(3);
});
specify('test mocha life cycle2', () => {
expect(3).to.be.eq(3);
});
});
typescript test mocha
test sum demo
所有测试用例执行前执行
每一个测试用例执行前执行
✓ test mocha life cycle
每一个测试用例执行后运行
每一个测试用例执行前执行
✓ test mocha life cycle2
每一个测试用例执行后运行
所有测试用例执行后执行
2 passing (19ms)
context('test async/await context', () => {
specify('await', async () => {
const data = new Promise((resolve) => {
setTimeout(() => {
resolve('延迟一秒');
}, 1000);
});
console.info(await data);
});
});
typescript test mocha
test async/await context
延迟一秒
✓ await (1008ms)
1 passing (1s)
使用only关键字,可以运行指定的测试条件,下面的代码中,由于给’test async/await context’使用了only,所以只会执行该测试套件
context('test sum demo', () => {
before(() => {
console.log('所有测试用例执行前执行');
});
beforeEach(() => {
console.info('每一个测试用例执行前执行');
});
afterEach(() => {
console.info('每一个测试用例执行后运行');
});
after(() => {
console.info(`所有测试用例执行后执行`);
});
specify('test mocha life cycle', () => {
expect(3).to.be.eq(3);
});
specify('test mocha life cycle2', () => {
expect(3).to.be.eq(3);
});
});
context.only('test async/await context', () => {
specify('await', async () => {
const data = new Promise((resolve) => {
setTimeout(() => {
resolve('延迟一秒');
}, 1000);
});
console.info(await data);
});
});
typescript test mocha
test async/await context
延迟一秒
✓ await (1006ms)
1 passing (1s)
context('test sum demo', () => {
before(() => {
console.log('所有测试用例执行前执行');
});
beforeEach(() => {
console.info('每一个测试用例执行前执行');
});
afterEach(() => {
console.info('每一个测试用例执行后运行');
});
after(() => {
console.info(`所有测试用例执行后执行`);
});
specify('test mocha life cycle', () => {
expect(3).to.be.eq(3);
});
specify('test mocha life cycle2', () => {
expect(3).to.be.eq(3);
});
});
context.only('test async/await context', () => {
specify('await', async () => {
const data = new Promise((resolve) => {
setTimeout(() => {
resolve('延迟一秒');
}, 1000);
});
console.info(await data);
});
});
context.only('test async/await context two', () => {
specify('await', async () => {
const data = new Promise((resolve) => {
setTimeout(() => {
resolve('延迟二秒');
}, 2000);
});
console.info(await data);
});
});
typescript test mocha
test async/await context
延迟一秒
✓ await (1005ms)
test async/await context two
延迟二秒
✓ await (2006ms)
2 passing (3s)
context.only('test async/await context', () => {
specify('await', async () => {
const data = new Promise((resolve) => {
setTimeout(() => {
resolve('延迟一秒');
}, 1000);
});
console.info(await data);
});
specify.only('await only', async () => {
const data = new Promise((resolve) => {
setTimeout(() => {
resolve('only');
}, 1000);
});
console.info(await data);
});
});
typescript test mocha
test async/await context
only
✓ await only (1006ms)
1 passing (1s)
使用skip关键字,可以跳过指定的测试套件或测试用例,被跳出的会被标记
context('test sum demo', () => {
before(() => {
console.log('所有测试用例执行前执行');
});
beforeEach(() => {
console.info('每一个测试用例执行前执行');
});
afterEach(() => {
console.info('每一个测试用例执行后运行');
});
after(() => {
console.info(`所有测试用例执行后执行`);
});
specify('test mocha life cycle', () => {
console.info('我存在');
expect(3).to.be.eq(3);
});
specify.skip('test mocha life cycle2', () => {
console.info('我被跳过');
expect(3).to.be.eq(3);
});
});
context.skip('test async/await context', () => {
specify('await', async () => {
const data = new Promise((resolve) => {
setTimeout(() => {
resolve('延迟一秒');
}, 1000);
});
console.info(await data);
});
specify('await only', async () => {
const data = new Promise((resolve) => {
setTimeout(() => {
resolve('我先执行');
}, 1000);
});
console.info(await data);
});
前缀为 '-'的是被跳过的标记
typescript test mocha
test sum demo
所有测试用例执行前执行
每一个测试用例执行前执行
我存在
✓ test mocha life cycle
每一个测试用例执行后运行
- test mocha life cycle2
所有测试用例执行后执行
test async/await context
- await
- await only
1 passing (32ms)
3 pending
使用this.retries可以设置重试次数
context('text retries demo', function () {
let sum = 0;
specify('should retries 3', function () {
sum += 1;
this.retries(3);
console.info(`重试次数${sum}`);
throw new Error('throw error');
})
});
typescript test mocha
text retries demo
重试次数1
重试次数2
重试次数3
重试次数4
1) should retries 3
0 passing (39ms)
1 failing
1) typescript test mocha
text retries demo
should retries 3:
Error: throw error
at Context. (study-node-modul-ts/mocha.ts:42:13)
利用逻辑代码,生成动态的测试用例
function add() {
return Array.prototype.slice.call(arguments).reduce(function (prev: any, curr: any) {
return prev + curr;
}, 0);
}
context('add()', function () {
const chunk = [
{ args: [1, 2], expected: 3 },
{ args: [1, 2, 3], expected: 6 },
{ args: [1, 2, 3, 4], expected: 10 }
];
chunk.forEach(function (test) {
specify('correctly adds ' + test.args.length + ' args', function () {
expect(add.apply(null, test.args)).to.be.eq(test.expected);
});
});
});
add()
✓ correctly adds 2 args
✓ correctly adds 3 args
✓ correctly adds 4 args
3 passing (24ms)
context('test slow demo', function () {
specify('should fast', function () {
this.slow(1000);
});
specify('should normal', async function () {
this.slow(1000);
await new Promise(resolve => {
setTimeout(() => {
resolve('----');
},1500);
});
});
specify('should slow', async function () {
this.slow(1500);
await new Promise(resolve => {
setTimeout(() => {
resolve('----');
}, 2000);
});
});
});
test slow demo
✓ should fast
✓ should normal (1504ms)
✓ should slow (2003ms)
3 passing (4s)
context('test time out', function () {
specify('should overtime', function (done) {
this.timeout(50);
setTimeout(done, 100);
});
specify('no should overtime', function (done) {
setTimeout(done, 20);
});
});
test time out
✓ should overtime (103ms)
✓ no should overtime
2 passing (142ms)
context('test time out 50ms', function () {
this.timeout(50);
specify('should overtime', function (done) {
setTimeout(done, 100);
});
specify('no should overtime', function (done) {
setTimeout(done, 20);
});
});
context('test time out 50ms', function () {
this.timeout(50);
specify('should overtime', function (done) {
setTimeout(done, 20);
});
specify('no should overtime', function (done) {
setTimeout(done, 20);
});
});
test time out 50ms
✓ should overtime (101ms)
✓ no should overtime
test time out 50ms
✓ should overtime
✓ no should overtime
4 passing (190ms)
在接下来的测试中,我们均使用以下测试代码
const { expect } = require('chai');
const fun = (data)=>{
return data;
};
context('test tty style', function () {
before('before', function () {
console.info('before');
});
after('after', function () {
console.info('after');
})
specify('should return 3', function () {
expect(fun(3)).to.be.eq(3);
});
specify('should return 2', function () {
expect(fun(3)).to.be.eq(2);
});
});
默认的测试报告风格
dot matrix视图报告使用一系列的字符来表示报告的结果,失败的测试使用红色的!来表示,pending测试使用蓝色的,来表示。慢的测试用黄色的.来表示。这个终端输出的内容最少
我的ubuntu没有安装matrix样式,这里就借用官方的图
有趣的动画风格
https://en.wikipedia.org/wiki/Test_Anything_Protocol
landing strip飞机降落的跑道,测试报告就是像一架飞机轨道一样的视图
进度报告器实现了一个简单的进度栏
测试完成(是否失败)后,JSON报告程序将输出一个大的JSON对象
输出的也是一个json,不同测试用例以换行符进行分割
这个报告只显示测试的整体情况,但是仍然会输出错误和失败的情况。和**–watch**选项结合使用最好,可视化更高一些?
生成一个包含html的body内容的测试报告
我们将它让如完整的html进行展示
在vscode中展示如下
只有在浏览器中使用Mocha的时候才能生成这种报告
.opts格式的文件在未来版本中会被弃用,官方目前支持的格式有
- .js
- .yml
- jsonc
- package.json
更多格式可以在mocha官方的github上查看,在这里我们使用官方提供的.js配置为例
'use strict';
module.exports = {
diff: true, //抛错时展示差异
extension: ['js'], //需要加载的文件扩展名
package: './package.json', //package.json路径
reporter: 'nyan', //测试报告风格
slow: 75, //慢速阀值,超出会警告
timeout: 2000, //超时时间,超出警告
ui: 'bdd', //测试风格
'watch-files': ['lib/**/*.js', 'test/**/*.js'], //执行测试的路径
'watch-ignore': ["node_modules", ".git"] //指定忽略文件
};
使用 --config命令指定配置文件路径
const { expect } = require('chai');
断言否定
context('test mocha not', function () {
specify('should pass ', function () {
expect(function () { }).to.not.throw();
});
specify('should fail', function () {
expect({ a: 1 }).to.not.have.property('a');
});
specify('should pass', function () {
expect([1, 2]).to.be.an('array').that.does.not.include(3);
});
});
在链中的所有以及断言中启用点和括号符号。.property.include
context('test mocha nested', function () {
specify('should pass ', function () {
expect({ a: { b: ['x', 'y'] } }).to.have.nested.property('a.b[1]');
});
specify('should pass', function () {
expect({ a: { b: ['x', 'y'] } }).to.nested.include({ 'a.b[1]': 'y' });
});
specify('should fail', function () {
expect({ a: { b: ['x', 'y'] } }).to.have.nested.property('a.b[2]');
});
});
使链中的所有和断言忽略继承和原型上的属性。.property.include
context('test mocha nested', function () {
Object.prototype.b = 2;
specify('should pass ', function () {
expect({ a: 1 }).to.have.own.property('a');
});
specify('should pass', function () {
expect({ a: 1 }).to.have.own.property('b');
});
specify('should fail', function () {
expect({ a: 1 }).to.not.have.own.property('b');
});
});
断言成员排序
context('test mocha ordered', function () {
specify('should pass ', function () {
expect([1, 2]).to.have.ordered.members([1, 2])
.but.not.have.ordered.members([2, 1]);
});
specify('should pass', function () {
expect([1, 2, 3]).to.include.ordered.members([1, 2])
.but.not.include.ordered.members([2, 3])
});
specify('should fail', function () {
expect([1, 2]).to.have.ordered.members([2, 3])
.but.not.have.ordered.members([1, 2]);
});
});
兼容任何类型
所有类型
context('test mocha ordered', function () {
specify('should pass ', function () {
expect({ a: 1, b: 2 }).to.not.have.any.keys('a', 'd');
});
specify('should pass', function () {
expect({ a: 1, b: 2 }).to.not.have.all.keys('a', 'b');
});
specify('should fail', function () {
expect({ a: 1, b: 2 }).to.have.all.keys('a', 'b');
});
});
判断数据类型
context('test mocha .a', function () {
specify('should return pass ', function () {
expect('foo').to.be.a('string');
});
specify('should return pass', function () {
expect({ a: 1 }).to.be.an('object');
});
specify('should return pass', function () {
expect(null).to.be.a('null');
});
specify('should return pass ', function () {
expect(undefined).to.be.an('undefined');
});
specify('should return pass', function () {
expect(new Error).to.be.an('error');
});
specify('should return pass', function () {
expect(Promise.resolve()).to.be.a('promise');
});
specify('should return fail', function () {
expect(1).to.be.a('promise');
});
});
.a的复杂断言
context('test mocha .a', function () {
specify('should return pass ', function () {
expect([1, 2, 3]).to.be.an('array').that.includes(2);
});
specify('should return pass', function () {
expect([]).to.be.an('array').that.not.empty;
});
specify('should return pass', function () {
expect([]).to.be.an('array').that.is.empty;
});
specify('should return pass', function () {
expect({ b: 2 }).to.have.a.property('b');
});
});
断言是否包含
context('test mocha .include', function () {
specify('should return pass ', function () {
expect('foobar').to.include('foo');
});
specify('should return pass', function () {
expect([1, 2, 3]).to.include(2);
});
specify('should return pass', function () {
expect({ a: 1, b: 2, c: 3 }).to.include({ a: 1, b: 2 });
});
specify('should return pass', function () {
expect(new Set([1, 2])).to.include(2);
});
specify('should return pass', function () {
expect(new Map([['a', 1], ['b', 2]])).to.include(2);
});
specify('should return pass', function () {
expect([1, 2, 3]).to.be.an('array').that.includes(2);
});
specify('should return pass', function () {
expect({ a: { b: ['x', 'y'] } }).to.nested.include({ 'a.b[1]': 'y2' });
});
});
断言是否为真
context('test mocha .ok', function () {
specify('should return pass ', function () {
expect(1).to.be.ok;
});
specify('should return pass', function () {
expect(true).to.be.ok
});
specify('should return pass', function () {
expect(0).to.not.be.ok;
});
specify('should return pass', function () {
expect(false).to.not.be.ok;
});
specify('should return pass', function () {
expect(null).to.not.be.ok;
});
specify('should return pass', function () {
expect(undefined).to.not.be.ok;
});
specify('should return pass', function () {
expect(false, 'this error').to.be.ok
});
});
断言是否为真(严格类型,类似JS的===)
context('test mocha .true', function () {
specify('should return pass ', function () {
expect(true).to.be.true;
});
specify('should return pass', function () {
expect(false).to.not.be.true;
});
specify('should return pass', function () {
expect(1).to.not.be.true;
});
specify('should return pass', function () {
expect(0, 'nooo why fail??').to.be.true;
});
});
断言目标严格等于false
context('test mocha .false', function () {
specify('should return pass ', function () {
expect(false).to.be.false;
});
specify('should return pass', function () {
expect(true, 'nooo why fail??').to.be.false;
});
});
context('test mocha .null', function () {
specify('should return pass ', function () {
expect(null).to.be.null;
});
specify('should return pass', function () {
expect(42, 'nooo why fail??').to.be.null;
});
});
Testingcoverage(测试覆盖),指测试系统覆盖被测试系统的程度,一项给定测试或一组测试对某个给定系统或构件的所有指定测试用例进行处理所达到的程度
- 功能覆盖率= 至少被执行一次的测试功能点数/测试功能点总数 (功能点)
- 需求覆盖率= 被验证到的需求数量 /总的需求数量 (需求)
- 覆盖率= 至少被执行一次的测试用例数/ 应执行的测试用例总数 (测试用例)
- 语句覆盖率= 至少被执行一次的语句数量/有效的程序代码行数
- 判定覆盖率= 判定结果被评价的次数 / 判定结果总数
- 条件覆盖率= 条件操作数值至少被评价一次的数量 / 条件操作数值的总数
- 判定条件覆盖率= 条件操作数值或判定结果至少被评价一次的数量/(条件操作数值总数+判定结果总数)
- 上下文判定覆盖率= 上下文内已执行的判定分支数和/(上下文数*上下文内的判定分支总数)
- 基于状态的上下文入口覆盖率= 累加每个状态内执行到的方法数/(状态数*类内方法总数)
- 分支条件组合覆盖率= 被评测到的分支条件组合数/分支条件组合数
- 路径覆盖率= 至少被执行一次的路径数/程序总路径数
测试评估可以说贯穿整个软件测试过程,可以在测试每个阶段结束前进行,也可以在测试过程中某一个时间进行,目的只有一个,提高测试覆盖度,保证测试的质量。通过不断的测试覆盖度评估或测试覆盖率计算,及时掌握测试的实际状况与测试覆盖度目标的差距,及时采取措施,就可以提高测试的覆盖度。
测试覆盖度的评估依赖于不同的测试阶段或不同的测试方法。如在单元测试中,测试覆盖率是建立在被测试的代码行、程序分支和程序路径等的度量之上,从软件质量保证的要求出发,单元测试的覆盖率要达到80%之上。白盒测试方法主要以程序语句、判定-条件、条件组合和(基本)路径等覆盖率来衡量,和单元测试是吻合的。而在系统功能测试中,则以功能点、测试用例、需求数等覆盖率来衡量。
要获得、提高测试覆盖率,常常需要借助测试工具。下面就以两个测试工具为例
nyc是一个用于检测具有Istanbul覆盖范围的代码的命令行工具
npm i -g nyc
mocha的配置文件
'use strict';
module.exports = {
diff: true, //抛错时展示差异
extension: ['js'], //需要加载的文件扩展名
package: './package.json', //package.json路径
reporter: 'spec', //测试报告风格
slow: 75, //慢速阀值,超出会警告
timeout: 2000, //超时时间,超出警告
ui: 'bdd', //测试风格
'watch-files': ['lib/**/*.js', 'test/**/*.js', './*.js'], //执行测试的路径
'watch-ignore': ["node_modules", ".git"], //指定忽略文件
// "--watch": true
};
//示例代码
const { expect } = require('chai');
context('test mocha .null', function () {
specify('should return pass ', async function () {
expect(null).to.be.null;
var fs = require("fs");
// 异步读取
console.info(await fs.readFile('input.txt'));
});
specify('should return pass', function () {
expect(42, 'nooo why fail??').to.be.null;
});
});
//执行命令
nyc mocha test --config ./test/.mocharc.js
同样的,我们可以以html的形式进行导出展示
更多的使用方法请参见官方网站https://istanbul.js.org/docs/advanced/alternative-reporters/
祝大家周末愉快~~~