Mocha 复用测试用例

在使用 Mocha 进行单元测试时,经常会有复用测试用例的需求。例如定义了一组测试用例后,希望在多种环境下都使用这组用例进行测试。

1. 测试环境

首先简单地搭建一个测试环境。

创建项目:

1
2
3
4
mkdir mocha-reuse
cd mocha-reuse
yarn init -y
yarn add -D mocha

项目目录结构如下:

  • index.js
  • test
    • index.test.js

其中 index.js 内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// index.js
module.exports = class Human {
constructor(name, age) {
if (typeof name === 'string') {
this.name = name;
} else if (name instanceof Buffer) {
this.name = name.toString();
} else {
throw TypeError('Name can only be string or buffer');
}
if (typeof age === 'number') {
this.age = age;
} else if (typeof age === 'string') {
this.age = Number(age);
} else {
throw TypeError('Age can only be number or string');
}
}
toString() {
return `${this.name}: ${this.age}`;
}
}

逻辑非常简单,Human 类构造时需要两个参数,name 支持 stringBufferage 支持 numberstring

测试内容是,构造一个 Human 实例后测试 nameagetoString() 是否正确。

2. 无复用版本

为了提高测试覆盖率,必须使用每一种可能的参数组合实例化 Human

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
// test/index.test.js
const assert = require('assert');
const Human = require('../index');

describe('Test with string name and number age', () => {
let human;
before(() => {
human = new Human('A', 1);
});
it('Check name', () => {
assert(human.name === 'A');
});
it('Check age', () => {
assert(human.age === 1);
});
it('Check toString', () => {
assert(human.toString() === 'A: 1');
});
});

describe('Test with buffer name and number age', () => {
let human;
before(() => {
human = new Human(Buffer.from('A'), 1);
});
it('Check name', () => {
assert(human.name === 'A');
});
it('Check age', () => {
assert(human.age === 1);
});
it('Check toString', () => {
assert(human.toString() === 'A: 1');
});
});

describe('Test with string name and string age', () => {
let human;
before(() => {
human = new Human('A', '1');
});
it('Check name', () => {
assert(human.name === 'A');
});
it('Check age', () => {
assert(human.age === 1);
});
it('Check toString', () => {
assert(human.toString() === 'A: 1');
});
});

describe('Test with buffer name and string age', () => {
let human;
before(() => {
human = new Human(Buffer.from('A'), '1');
});
it('Check name', () => {
assert(human.name === 'A');
});
it('Check age', () => {
assert(human.age === 1);
});
it('Check toString', () => {
assert(human.toString() === 'A: 1');
});
});

可以看到,除了初始化不同之外,所有的测试样例都是一样的,因此我们必须想办法复用测试用例

3. 复用测试用例

test 目录下创建新的文件 share.js,将复用的用例放在里面:

1
2
3
4
5
6
7
8
9
10
11
12
13
const assert = require('assert');

module.exports = function () {
it('Check name', function () {
assert(this.human.name === 'A');
});
it('Check age', function () {
assert(this.human.age === 1);
});
it('Check toString', function () {
assert(this.human.toString() === 'A: 1');
});
};

此时目录结构变为:

  • index.js
  • test
    • index.test.js
    • share.js

index.test.js 变为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
const Human = require('../index');
const check = require('./share');

describe('Test with string name and number age', () => {
before(function () {
this.human = new Human('A', 1);
});
check();
});

describe('Test with buffer name and number age', () => {
before(function () {
this.human = new Human(Buffer.from('A'), 1);
});
check();
});

describe('Test with string name and string age', () => {
before(function () {
this.human = new Human('A', '1');
});
check();
});

describe('Test with buffer name and string age', () => {
before(function () {
this.human = new Human(Buffer.from('A'), '1');
});
check();
});

要注意的是,这里在所有涉及 this 的函数上,不要使用箭头函数。

4. 参考

Shared Behaviours