ㅤㅤㅤ
ㅤㅤㅤ
ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ(不应当急于求成,应当去熟悉自己的研究对象,锲而不舍,时间会成全一切。凡事开始最难,然而更难的是何以善终。——莎士比亚)
ㅤㅤㅤ
ㅤㅤㅤ
ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ

什么是单元测试

指对软件中的最小可测试单元进行检查和验证。对于单元测试中单元的含义,一般来说,要根据实际情况去判定其具体含义,如C语言中单元指一个函数,Java里单元指一个类,图形化的软件中可以指一个窗口或一个菜单等。总的来说,单元就是人为规定的最小的被测功能模块。单元测试是在软件开发过程中要进行的最低级别的测试活动,软件的独立单元将在与程序的其他部分相隔离的情况下进行测试

为什么需要单元测试

  • 保障软件项目质量,减少企业损失

没有经过测试的代码犹如在战场上裸奔的士兵。人不是机器,即便是测试人员,也不能保证每个功能是否完全稳定,因为只有先让开发者先去消费它,才能给予最重要的质量保证

  • 减少企业测试成本,避免投入冗余的测试资源

高质量的单元测试远远比人工测试更高效。在’全自动化’,'人工智能’的大时代,机器远远比人工更高效,与其投入大量的测试资源,比如花时间编写高效稳定的单元测试

  • 更精准的定位程序中的bug

测试基于表面去判断问题,而只有开发是知其所以然的。毕竟代码是开发者编写的,测试人员的优势只在于时间和对功能的熟悉度,若有了单元测试,不仅能节省测试人员的时间,还能够减少程序bug数量

  • 避免开发者对测试人员产生依赖,完全不关心其代码的测试

人都是有惰性的,因为 测试人员的存在,导致开发者对自己编写的代码质量漠不关心。对测试人员过度依赖的项目必定是不稳定和不安全的

  • 更高的可维护性

由于缺少单元测试,程序中存在着’坏代码’。一些’千年老bug’,'临时拆东墙补西墙的解决方案’使得代码的维护和重构难度愈来愈大,牵一发而动全身。通过单元测试尽可能的提高代码覆盖率

  • 了解你的代码性能

虽然这不是单元测试的主要目的,但在单元测试中编写运行指定场景的性能分析会非常容易,在现有的代码测试中快速复制一份能让指定对象运行起来的测试代码,运行它测量性能,修改代码查看性能的变化情况,像德芙一样丝滑

  • 证明你的工作已完成

无论需求驱动还是测试驱动,你的任务通常都是完成一系列代码,实现具体的一系列功能。那么当你提交代码时,你怎么证明你的提交时这个功能中的未完成部分还是已经全部完成,如何证明你功能是否经过测试可以随时合并到生产环境,换句话说,你怎么证明你的代码完全按照实现的“约定行为”运行的。
当我们仅使用口头或者书面形式描述“约定行为”时,非常困难的一点是如何消除自然语言中的歧义,如何避免不同的人们基于各自不同背景知识脑补出不同的内容导致隐形的分歧。而使用代码来描述行为规约的一个好处就是计算机运行需要指定的指令,不会存在二义性。因此用一段运行的代码可以完整无误的证明你的代码是完成的

单元测试原则

  • 单一职责

如果一段代码承担的职责越多,为其编写单元的时候就要构建更多的输入数据,然后推测它的输入。比如,一段代码中既包含数据库的链接,也包含查询,那么为它编写测试用例就要同事关注数据库连接和数据库查询。较好的方式是将这两种职责进行解耦分离,变成两个单一职责的方法,分别测试数据库连接和数据库查询

  • 接口抽象

通过对程序代码进行接口抽象后,我们可以针对接口进行测试,而具体代码实现的变化不影响为接口编写的单元测试

  • 层次分离

层次分离实际上是单一职责的一种实现。在MVC结构的应用中,就是典型的层次分离模型,如果不分离各个层次,无法想象这个代码该如何切入测试。通过分层之后,可以逐层测试,逐层保证

对于开发者来说,不仅要编写单元测试,还应当编写可测试代码

单元测试模式

BDD和TDD区别

  • TDD 测试驱动开发(Test-Driven Development)

测试驱动开发是敏捷开发中的一项核心实践和技术,也是一种设计方法论。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 相反
  • BDD 行为驱动开发(Behavior Driven Development)

行为驱动开发是一种敏捷软件开发的技术,它鼓励软件项目中的开发者、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的通用语言是一种近乎自然语言的描述软件的形式。传统的开发模式中,客户很难从技术层面理解问题,开发人员很难从业务需求考虑问题,基于这种通用语言形式可以尽可能的避免客户和开发者在沟通上的障碍,实现客户和开发者同时定义系统的需求。避免了因为理解需求不充分而带来的不必必要的工作量。

Nodejs断言库

  • assert

node中绝大多数单元测试的基础,它可以测试一个条件,如果条件未满足,则抛出错误,很多第三方框架都用了assert模块,但缺点是,一旦检查失败,将会抛出异常停止整个应用,这对于大规模断言检查时,并不友好,因为更通用的做法是,记录抛出的异常,并继续执行,生成测试报告
特性

  • 简单
  • 轻量
  • 原生
var assert = require('assert');function add (a, b) {return a + b;
}var expect = add(1, 2);
assert(expect === 3, 'should return 3');
  • should.js

它类似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);
});
  • chai

当前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);

NodejS测试框架

  • mocha

相对比较新,运行在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);});});
});
  • vows

一个结构化更强的,且更容易理解的和维护的测试框架,拥有自己的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.jsArray#indexOf()✓ should return -1 when the value is not present1 passing (9ms)

mocha内置了assert断言库,但如果抛出异常,则会停止程序执行,建议使用其他断言库,在这里我们选择搭配chai


下面总结了一些mocha的常用API

  • mocha默认BDD接口,所以提供了以下四种生命周期函数
  • 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 mochatest sum demo
所有测试用例执行前执行
每一个测试用例执行前执行✓ test mocha life cycle
每一个测试用例执行后运行
每一个测试用例执行前执行✓ test mocha life cycle2
每一个测试用例执行后运行
所有测试用例执行后执行2 passing (19ms)

  • 支持async/await
  context('test async/await context', () => {specify('await', async () => {const data = new Promise((resolve) => {setTimeout(() => {resolve('延迟一秒');}, 1000);});console.info(await data);});});
typescript test mochatest 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 mochatest 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 mochatest async/await context
延迟一秒✓ await (1005ms)test async/await context two
延迟二秒✓ await (2006ms)2 passing (3s)
  • 在嵌套的测试套件中使用only
  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 mochatest 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 mochatest sum demo
所有测试用例执行前执行
每一个测试用例执行前执行
我存在✓ test mocha life cycle
每一个测试用例执行后运行- test mocha life cycle2
所有测试用例执行后执行test async/await context- await- await only1 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 mochatext retries demo
重试次数1
重试次数2
重试次数3
重试次数41) should retries 30 passing (39ms)1 failing1) typescript test mochatext retries demoshould retries 3:Error: throw errorat Context.<anonymous> (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 args3 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 overtime2 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 overtimetest time out 50ms✓ should overtime✓ no should overtime4 passing (190ms)

mocha终端风格

在接下来的测试中,我们均使用以下测试代码

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);});
});
  • –reporter spec

默认的测试报告风格

  • –reporter dot matrix

dot matrix视图报告使用一系列的字符来表示报告的结果,失败的测试使用红色的!来表示,pending测试使用蓝色的,来表示。慢的测试用黄色的.来表示。这个终端输出的内容最少
我的ubuntu没有安装matrix样式,这里就借用官方的图

  • –reporter nyan

有趣的动画风格

  • –reporter TAP

https://en.wikipedia.org/wiki/Test_Anything_Protocol

  • –reporter landing strip

landing strip飞机降落的跑道,测试报告就是像一架飞机轨道一样的视图

  • –reporter list

当测试用例通过或失败时,列表报告器将输出一个简单的规格列表,并在输出的底部输出失败的详细信息

  • –reporter progress

进度报告器实现了一个简单的进度栏

  • –reporter json

测试完成(是否失败)后,JSON报告程序将输出一个大的JSON对象

  • –reporter json-stream

输出的也是一个json,不同测试用例以换行符进行分割

  • –reporter min --watch

这个报告只显示测试的整体情况,但是仍然会输出错误和失败的情况。和**–watch**选项结合使用最好,可视化更高一些?

  • –reporter doc

生成一个包含html的body内容的测试报告

我们将它让如完整的html进行展示

  • –reporter markdown

生成markdown格式的报告

在vscode中展示如下

  • –reporter html

只有在浏览器中使用Mocha的时候才能生成这种报告


…等等还有很多展示风格,具体的大家可以查看官网


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命令指定配置文件路径


chai断言API

const { expect } = require('chai');
  • .not

断言否定

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);});
});

  • .nested

在链中的所有以及断言中启用点和括号符号。.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]');});
});

  • own

使链中的所有和断言忽略继承和原型上的属性。.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');});
});


  • .ordered

断言成员排序

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]);});
});


  • .any

兼容任何类型

  • .all

所有类型

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');});
});


  • .a

判断数据类型

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');});
});


  • .include

断言是否包含

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' });});
});


  • .ok

断言是否为真

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});
});


  • .true

断言是否为真(严格类型,类似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

断言目标严格等于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;});
});


  • .null
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;});
});


  • 等等等,由于chai的断言api非常丰富,更多的,博主就不在这里一一列出了,大家可以去官网查看

测试覆盖率

Testingcoverage(测试覆盖),指测试系统覆盖被测试系统的程度,一项给定测试或一组测试对某个给定系统或构件的所有指定测试用例进行处理所达到的程度

软件测试覆盖率基本计算公式
  • 功能覆盖率= 至少被执行一次的测试功能点数/测试功能点总数 (功能点)
  • 需求覆盖率= 被验证到的需求数量 /总的需求数量 (需求)
  • 覆盖率= 至少被执行一次的测试用例数/ 应执行的测试用例总数 (测试用例)
  • 语句覆盖率= 至少被执行一次的语句数量/有效的程序代码行数
  • 判定覆盖率= 判定结果被评价的次数 / 判定结果总数
  • 条件覆盖率= 条件操作数值至少被评价一次的数量 / 条件操作数值的总数
  • 判定条件覆盖率= 条件操作数值或判定结果至少被评价一次的数量/(条件操作数值总数+判定结果总数)
  • 上下文判定覆盖率= 上下文内已执行的判定分支数和/(上下文数*上下文内的判定分支总数)
  • 基于状态的上下文入口覆盖率= 累加每个状态内执行到的方法数/(状态数*类内方法总数)
  • 分支条件组合覆盖率= 被评测到的分支条件组合数/分支条件组合数
  • 路径覆盖率= 至少被执行一次的路径数/程序总路径数

测试评估可以说贯穿整个软件测试过程,可以在测试每个阶段结束前进行,也可以在测试过程中某一个时间进行,目的只有一个,提高测试覆盖度,保证测试的质量。通过不断的测试覆盖度评估或测试覆盖率计算,及时掌握测试的实际状况与测试覆盖度目标的差距,及时采取措施,就可以提高测试的覆盖度。

测试覆盖度的评估依赖于不同的测试阶段或不同的测试方法。如在单元测试中,测试覆盖率是建立在被测试的代码行、程序分支和程序路径等的度量之上,从软件质量保证的要求出发,单元测试的覆盖率要达到80%之上。白盒测试方法主要以程序语句、判定-条件、条件组合和(基本)路径等覆盖率来衡量,和单元测试是吻合的。而在系统功能测试中,则以功能点、测试用例、需求数等覆盖率来衡量。

要获得、提高测试覆盖率,常常需要借助测试工具。下面就以两个测试工具为例


mocha的测试覆盖率

  • nyc
    官网:https://istanbul.js.org/
    github:https://github.com/istanbuljs/nyc

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/
祝大家周末愉快~~~

Noejs Mocha测试框架相关推荐

  1. Truffle测试框架

    Truffle测试框架 Truffle 有一个标准的自动化测试框架,让你可以非常方便地测试您的合约.这个框架允许您以两种不同的方式编写简单可控的测试: 1. 在JavaScript中, 用于执行来自外 ...

  2. Protractor AngularJS测试框架教程

    Protractor是一个建立在WebDriverJS基础上的端到端(E2E)的AngularJS JavaScript Web应用程序测试框架.Protractor全自动化真实的模拟用户在真正的浏览 ...

  3. javascript测试框架mocha

    node测试框架mocha 简单.灵活.有趣,mocha是一个功能丰富的javascript测试框架,运行在node和浏览器中,使异步测试变得更加简单有趣.http://mochajs.org/ 安装 ...

  4. mocha检测c语言,前端测试框架mocha使用小结

    安装 npm i -g mocha npm i chai -D //断言库 模块测试 比如有一个add函数 //add.js function add(a, b){ return a + b } mo ...

  5. testem方便的web tdd 测试框架使用

    备注: 单元测试,对于日常的开发是比较重要的,testem 简化了我们的代码编写,以及运行. 主要特性: a. 支持的测试框架有:jasmine quint mocha buster.js ,同时也包 ...

  6. e2e测试框架之Cypress

    谈起web自动化测试,大家首先想到的是Selenium!随着近几年前端技术的发展,出现了不少前端测试框架,这些测试框架大多并不依赖于Selenium,这一点跟后端测试框架有很大不同,如Robot Fr ...

  7. 测试框架 Jest 实例教程

    Jest 是由 Facebook 开源出来的一个测试框架,它集成了断言库.mock.快照测试.覆盖率报告等功能.它非常适合用来测试 React 代码,但不仅仅如此,所有的 js 代码都可以使用 Jes ...

  8. 【Karma】多环境自动测试框架 -- 基础教程

    介绍 前身 Testacular, AngularJs Team 创建出来的. 以下是官网对Karma的相关特点介绍 支持真实浏览器, 无浏览器PhantomJS 热更新,文件变化后自动测试 测试框架 ...

  9. mocha 测试 mysql_node项目mocha自动化测试的疑问

    测试框架:mocha 数据库:mysql和mongodb 疑问1. 如何控制多个测试用例的运行顺序?用例写多了,A用例把数据变成了状态1,有些后面的用例基于这个状态1的数据进行查询判断,才能使得后面的 ...

最新文章

  1. 使用Kubespray部署Kubernetes集群
  2. Python源码学习:Python函数浅析-函数闭包
  3. python装饰器实现对异常代码出现进行监控
  4. CentOS中怎样卸载旧版本Git并安装高版本Git
  5. 计算机教师教学心得体会,信息技术教师教学的一点体会
  6. linux如何创建共享内存,linux实现共享内存同步的四种方法
  7. 【测试】禅道搭建在服务器上的操作步骤
  8. oracle利用正则表达式判断字符串只包含数字
  9. Java Synchronized的用法
  10. java包含某个字符串_JavaScript判断一个字符串是否包含指定子字符串的方法
  11. 计算机考试网站配置参数错误,计算机模拟考试系统_安装计算机考试模拟系统显示“数据库连接错误请联系管理员”是怎么回事...
  12. Go语言核心之美 4.1-函数声明
  13. python中round(18.67、-1)_python的round函数怎么用
  14. 关于 Kubernetes中NetworkPolicy(网络策略)方面的一些笔记
  15. 强化学习原理及应用作业之动态规划算法【SYSU_2023SpringRL】
  16. js通过对象通过value找key
  17. 使用uploadify进行上传
  18. ReentrantReadWriteLock读写锁(读多写少场景)
  19. oracle查询导致 gc等待,如何诊断Oracle RAC系统中的等待事件gc cr multi block request?...
  20. 负载均衡中SNA的ip-can(iscs使用示例i)

热门文章

  1. 一个刚看到的广告创意
  2. 知识点梳理IR-808 Biotin,Biotin 808-IR,IR808 生物素,红外吸收染料修饰生物素
  3. 360以不正当竞争为由起诉卡巴斯基
  4. 中国计划新建逾60万个5G基站
  5. 【 螺旋方阵 】所谓“螺旋方阵”,是指对任意给定的N,将1到N×N的数字从左上角第1个格子开始,按顺时针螺旋方向顺序填入N×N的方阵里。本题要求构造这样的螺旋方阵。
  6. AngularJS下一些JS的属性
  7. 《OpenCv视觉之眼》Python图像处理十六:Opencv图像处理实战一之图像中的硬币检测
  8. 华容道的求解算法重写
  9. SAP MDG —— 系统先导配置
  10. 【动物】生物的进化历程——2017年10月5日