测试驱动开发的一些技巧

当使用测试驱动开发,或者在开发中需要充分引入测试的时候,不仅仅需要重视小粒度的测试,还需要重视整体业务过程的测试,这往往需要更复杂的测试手段,比如对REST API的测试,以及多个API调用组成的集成测试。本文简单总结一下与测试有关的一些技术。本文以Node.js为例进行说明。

测量工具

测试首先要确保覆盖率,包括代码覆盖率、分支条件覆盖率和函数覆盖率。尽可能做到100%覆盖。要做到这一点,需要有工具进行支持。在Node.js中,可以选择使用nyc。进行测试后,会生成测试报告,根据测试报告,可以发现未被覆盖的代码,进而完善测试。

内存数据库

数据库是应用软件离不开的支撑环境,在测试时,我们不可能连接真正的数据库,但却需要对涉及数据库的功能进行测试,最好的办法是使用功能完全一致的内存数据库。如果使用MongoDB,可以使用针对MongoDB的内存数据库mongodb-memory-server。

在使用mongodb-memory-server进行测试时,可以使用process.env传递数据库的连接串,然后在需要的地方,使用MongoClient初始化连接。示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const {MongoMemoryServer} = require('mongodb-memory-server')
var assert = require('assert');
var MongoClient = require('mongodb').MongoClient;
describe('mongoUserCenter', function () {
let mongoServer;
before(async function () {
mongoServer = await MongoMemoryServer.create();
const mongoUri = mongoServer.getUri();
process.env.mongo_url=mongoUri
process.env.mongo_db="testdb"
})
after(async () => {
await mongoServer.stop();
});

})

在单元测试或者cucumber.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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
BeforeAll(async function () {

mongoServer = await MongoMemoryServer.create();
mongoUri = mongoServer.getUri();
const collections = [ "AUTH_USER_TB", "AUTH_ROLE_USER_TB"]
var mongodb = await MongoClient.connect(mongoUri);
var dbase = mongodb.db("testdb");
for (var i = 0; i < collections.length; i++) {
const collectionName = collections[i]//"YJS_XMGL_QDLH_tb"
const data = require('../../testData/' + collectionName + '.json')
const rows = data.rows
var collection = dbase.collection(collectionName);
const result = await collection.insertMany(rows)
}
await mongodb.close()
process.env.mongo_url = mongoUri
process.env.mongo_db = "testdb"

app = require('../../appmodule')

})

Before(async function () {

var mongodb = await MongoClient.connect(mongoUri);
var dbase = mongodb.db("testdb");
const collectionName = "YJS_XMGL_QDLH_tb"
const data = require('../../testData/' + collectionName + '.json')
const rows = data.rows
var collection = dbase.collection(collectionName);
const result = await collection.insertMany(rows)
await mongodb.close()
})
After(async function () {
////console.log("After")
var mongodb = await MongoClient.connect(mongoUri);
var dbase = mongodb.db("testdb");
var collection = dbase.collection('YJS_XMGL_QDLH_tb')
await collection.deleteMany({})
var vcollection = dbase.collection('verifications')
await vcollection.deleteMany({})
await mongodb.close()
})

AfterAll(async function () {
await mongoServer.stop();

})

RESTful API 测试框架

我们需要对RESTful API进行测试,希望不启动服务器,与上一节所说的内存数据库一起使用,可以完整测试整个应用。我们需要引入相应的框架,在node.js中可以使用supertest。

我们对app.js进行改造,将路由定义与侦听部分分开,路由定义部分作为模块导出:

1
2
3
4
5
6
7
8
9
app.use('/api',Routes)

//const port = 3020;

/* app.listen(port, () => {
//console.log(`Server listening on port ${port}`);
});
*/
module.exports=app

在侦听部分使用这个模块:

1
2
3
4
5
6
 const port = 3020;
const app=require('./appmodule')
app.listen(port, () => {
console.log(`Server listening on port ${port}`);
});

这样,模块部分可以使用supertest框架进行测试:

1
2
3
4
5
6
7
8
9
it('查询项目', async function () {
const response = await request(app)
.get('/verificationapi/getNoVerificationProjects')
.auth('jib678.qdlh', 'wangrk208.qdlh')
.set('Accept', 'application/json');
const projects = JSON.parse(response.text)
assert.equal(response.status, 200);
assert.equal(projects.length > 0, true);
});

使用上面这些技术进行组合,可以完备地测试整个应用。