在Node.js中为Restful API编写单元测试
作者:Scarletsky 发布时间:[ 2017/1/18 15:18:24 ] 推荐标签:软件测试 单元测试
现在,我们可以把 supertest 和其他测试框架整合起来了,我选择了 mocha 作为例子,因为它很经典,当你会用 mocha 之后,其他测试框架基本上难不倒你了。
const co = require('co')
const { ObjectId } = require('mongoose').Types
const config = require('../config')
const UserModel = require('../models/user')
const app = require('../app')
const request = require('supertest')(app)
describe('User API', function (){
// 为每个单元测试初始化数据
// 每个单元测试中可以通过 context 来访问相关的数据
beforeEach(function (done){
co(function* (){
self.user1 = yield UserModel.create({ username: 'user1' })
self.token = jwt.sign({ _id: self.user1._id }, config.jwtSecret, { expiresIn: 3600 })
done()
}).catch(err => {
console.log('err: ', err)
done()
})
})
// 正常情况下访问 /user
it('should get user info when GET /user with token', function (done){
const self = this
request
.get('/user')
.set('Authorization', self.token)
.expect(200)
.end((err, res) => {
res.body._id.should.equal(self.user1._id)
done()
})
})
// 非正常情况下访问 /user
it('should return 403 when GET /user without token', function (done){
request
.get('/user')
.expect(403, done)
})
// 访问 /users,登录用户和非登录用户都会得到相同的结果,所以不需要区别对待
it('should return user list when GET /users', function (done){
request
.get('/users')
.expect(200)
.end((err, res) => {
res.body.should.be.an.Array()
done()
})
})
// 访问 /users/:userId 也不需要区分登录和非登录状态
it('should return user info when GET /users/:userId', function (done){
const self = this
request
.get(`/users/${self.user1._id}`)
.expect(200)
.end((err, res) => {
res.body._id.should.equal(self.user1._id)
done()
})
})
// 访问不存在的用户,我们需要构造一个虚假的用户 id
it('should return 404 when GET /users/${non-existent}', function (done){
request
.get(`/users/${ObjectId()}`)
.expect(404, done)
})
// 正常情况下的用户注册不会带上 token
it('should return user info when POST /user', function (done){
const username = 'test user'
request
.post('/users')
.send({ username: username })
.expect(200)
.end((err, res) => {
res.body.username.should.equal(username)
done()
})
})
// 非法情况下的用户注册,带上了 token 的请求要判断为非法请求
it('should return 400 when POST /user with token', function (done){
const username = 'test user 2'
request
.post('/users')
.set('Authorization', this.token)
.send({ username: username })
.expect(400, done)
})
// 正常情况下更新用户信息,需要带上 token
it('should return 200 when PUT /user with token', function (done){
request
.put('/user')
.set('Authorization', this.token)
.send({ username: 'valid username' })
.expect(200, done)
})
// 非法情况下更新用户信息,如缺少 token
it('should return 400 when PUT /user without token', function (done){
request
.put('/user')
.send({ username: 'valid username' })
.expect(400, done)
})
})
可以看到,为 Restful API 编写单元测试还有一个优点,是可以轻易区分登录状态和非登录状态。如果要在用户界面中测试这些功能,那么需要不停地登录和注销,将会是一项累人的工作~
另外,上面的例子中基本都是对返回状态吗进行断言的,你可以按照自己的需要进行断言。
提示
你可以选择自己喜欢的断言库,我这里选择了 should.js,原因是好读。
个人认为 should.js 和其他断言库比起来有个缺点,是不好写。
value.should.xxx.yyy.zzz 这个形式和 assert.equal(value, expected) 相比不太直观。
另外由于 should.js 是通过扩展 Object.prototype 的原型来实现的,但 null 值是一个例外,它不能访问任何属性。
因此 should.js 在 null 上会失效。
一个变通的办法是 (value === null).should.equal(true) 。
$ npm test
User api
should get user info when GET /user with token
should return 403 when GET /user without token
should return user list when GET /users
should return user info when GET /users/:userId
should return 404 when GET /users/${non-existent}
should return user info when POST /user
should return 400 when POST /user with token
should return 200 when PUT /user with token
should return 400 when PUT /user without token
当我们运行测试时,看到自己编写的测试都通过时,心里都会非常踏实。
而当我们要对项目进行重构时,这些测试用例会帮我们发现重构过程中的问题,减少 Debug 时间,提升重构时的效率。
细节
如何连接测试数据库
在 Node.js 的环境下,我们可以设置环境变量 NODE_ENV=test ,然后通过这个环境变量去连接测试数据库,这样测试数据不会存在于开发环境下的数据库拉!
// config.js
module.exports = {
development: {},
production: {},
test: {}
}
// app.js
const ENV = process.NODE_ENV || 'development'
const config = require('./config')[ENV]
// connect db by config
如何清空测试数据库
清空数据库这种一次性的工作好放到 npm scripts 中处理,需要进行清空操作的时候直接运行 npm run resetDB 可以了。
需要注意的是,编写清空数据库脚本时必须判断环境变量 NODE_ENV ,以免误删 production 环境下的数据。
// resetDB.js
const env = process.NODE_ENV || 'development'
if (env === 'test' || env === 'development') {
// connect db and delete data
} else {
throw new Error('You can not run this script in production.')
}
// package.json
{
"scripts": {
"resetDB": "node scripts/resetDB.js"
},
// ...
}
何时清空测试环境的数据库
如果是按照上面的原则来生成测试数据的话,测试数据其实可以不用删掉的。
但由于测试数据会占用我们的空间,好还是把这些测试数据删掉。
那么,清空测试数据库这个操作在测试前执行好,还是测试后执行好?
我个人倾向于测试前删除,因为有时候我们需要进入数据库,查看测试数据的正确性。
如果在测试后清空测试数据库的话,我们没办法访问到测试数据了。
{
"scripts": {
"resetDB": "node scripts/resetDB.js",
"test": "NODE_ENV=test npm run resetDB && mocha --harmony"
},
// ...
}
本文内容不用于商业目的,如涉及知识产权问题,请权利人联系SPASVO小编(021-61079698-8054),我们将立即处理,马上删除。
相关推荐
iOS单元测试mocha、chai、sinon和istanbul实现百分之百的单元测试覆盖率关于单元测试的总结及思考编写更好的Java单元测试的7个技巧Android单元测试框架Robolectric3.0介绍(一)使用Kiwi单元测试总结单元测试如此重要,为什么你不知道Python单元测试??使用装饰器实现测试跳过和预期故障对Controller的单元测试写好单元测试的10个技巧单元测试的重要性Angular单元测试系列??Component、Directive、Pipe 以及ServiceAndroid单元测试的整理提升单元测试体验的利器--Mockito使用总结iOS UnitTest单元测试Vue的单元测试探索(二)
更新发布
功能测试和接口测试的区别
2023/3/23 14:23:39如何写好测试用例文档
2023/3/22 16:17:39常用的选择回归测试的方式有哪些?
2022/6/14 16:14:27测试流程中需要重点把关几个过程?
2021/10/18 15:37:44性能测试的七种方法
2021/9/17 15:19:29全链路压测优化思路
2021/9/14 15:42:25性能测试流程浅谈
2021/5/28 17:25:47常见的APP性能测试指标
2021/5/8 17:01:11热门文章
常见的移动App Bug??崩溃的测试用例设计如何用Jmeter做压力测试QC使用说明APP压力测试入门教程移动app测试中的主要问题jenkins+testng+ant+webdriver持续集成测试使用JMeter进行HTTP负载测试Selenium 2.0 WebDriver 使用指南