教程内容
采用测试驱动开发的方式,开发一个简单的 REST API,包括基本的 POST/GET/PUT/DELETE 操作
先编写好针对各个接口的测试代码,包括:
-
调用post接口插入一个对象
-
调用get接口获取某个对象的数据
-
调用get接口获取集合数据
-
调用put接口更新某个对象数据
-
调用get接口获取更新后的对象
-
调用delete接口删除对象
然后针对第一个测试进行代码编写,写完后执行测试,第一个测试通过后,继续开发下一个,再进行测试,这样迭代进行
测试框架采用 Mocha,WEB框架采用 Express.js 4,数据库使用 MongoDB
前期准备
安装好 Nodejs,Mongodb,配置好npm镜像
我使用的是:nvm 安装 nodejs、Mongodb Docker 镜像、淘宝的cnpm镜像,还安装了 supervisor(检测代码变更,自动加载)
创建项目
创建一个目录
test-rest-api
,然后在命令行下进入此目录
安装依赖
npm install mocha --save-dev
npm install expect.js --save-dev
npm install superagent --save-dev
npm install express --save
npm install mongo --save
npm install mongoskin --save
npm install body-parser --save
新建代码文件
-
express.js - api 代码
-
express.test.js - 测试代码
编写测试
express.test.js
内容:
var superagent = require('superagent')
var expect = require('expect.js')
var mongoskin = require('mongoskin')
var db = mongoskin.db('mongodb://@localhost:27017/test-rest', { safe: true })
describe('express rest api server', function() {
// 测试执行前清空数据库
before(function() {
db.collection('test').remove({})
var id
// --- 测试 post
it('post object', function(done) {
superagent.post('http://localhost:3000/collections/test')
.send({
name: 'Johns',
email: 'john@rpjs.co'
.end(function(e, res) {
// console.log(res.body)
expect(e).to.eql(null)
expect(res.body.ops.length).to.eql(1)
expect(res.body.ops[0]._id.length).to.eql(24)
id = res.body.ops[0]._id
done()
// --- 测试 get by ID
it('retrieves an object', function(done) {
superagent.get('http://localhost:3000/collections/test/' + id)
.end(function(e, res) {
// console.log(res.body)
expect(e).to.eql(null)
expect(typeof res.body).to.eql('object')
expect(res.body._id.length).to.eql(24)
expect(res.body._id).to.eql(id)
done()
})
// --- 测试 get 集合
it('retrieves a collection', function(done) {
superagent.get('http://localhost:3000/collections/test')
.end(function(e, res) {
// console.log(res.body)
expect(e).to.eql(null)
expect(res.body.length).to.be.above(0)
expect(res.body.map(function(item) {
return item._id
})).to.contain(id)
done()
})
// --- 测试 update 更新
it('updates an object', function(done) {
superagent.put('http://localhost:3000/collections/test/' + id)
.send({
name: 'Peter',
email: 'peter@yahoo.com'
.end(function(e, res) {
expect(e).to.eql(null)
done()
})
// --- 验证更新后的数据
it('checks an updated object', function(done) {
superagent.get('http://localhost:3000/collections/test/' + id)
.end(function(e, res) {
// console.log(res.body)
expect(e).to.eql(null)
expect(typeof res.body).to.eql('object')
expect(res.body._id.length).to.eql(24)
expect(res.body._id).to.eql(id)
expect(res.body.name).to.eql('Peter')
done()
})
// --- 测试 remove 删除
it('removes an object', function(done) {
superagent.del('http://localhost:3000/collections/test/' + id)
.end(function(e, res) {
// console.log(res.body)
expect(e).to.eql(null)
expect(res.body.msg).to.eql('success')
done()
})
运行测试
./node_modules/mocha/bin/mocha express.test.js
运行的结果一定是全部失败,因为还没有编写实际代码,下面就编写代码,使测试一个个的通过。
编写api
express.js
内容:
var express = require('express'),
mongoskin = require('mongoskin'),
bodyParser = require('body-parser')
var app = express()
app.use(bodyParser())
var db = mongoskin.db('mongodb://@localhost:27017/test-rest', {safe:true})
app.param('collectionName', function(req, res, next, collectionName){
req.collection = db.collection(collectionName)
return next()
app.get('/', function(req, res) {
res.send('欢迎')
// --- 后续功能代码区域
// -----------------
app.listen(3000)
上面是最基础的代码,连接到了数据库,启动了http服务
运行
node express.js
如果安装了 supervisor,就使用它来启动,之后改动 express.js 的话就不用重新启动了
supervisor express.js
启动后使用浏览器访问
http://localhost:3000/
可以看到欢迎信息,基础做好了,开始编写功能代码
添加 post 创建对象
在功能代码区添加:
app.post('/collections/:collectionName', function(req, res, next) {
req.collection.insert(req.body, {}, function(e, results){
if (e) return next(e)
res.send(results)
})
如果没使用 supervisor,记得重新执行
node express.js
执行测试
./node_modules/mocha/bin/mocha express.test.js
测试运行结果:
express rest api server
✓ post object (41ms)
1) retrieves an object
2) retrieves a collection
3) updates an object
4) checks an updated object
5) removes an object
1 passing (81ms)
5 failing
post 功能通过了
添加 get 根据ID取得对象信息
app.get('/collections/:collectionName/:id', function(req, res, next) {
req.collection.findById(req.params.id, function(e, result){
if (e) return next(e)
res.send(result)
})
重启、测试,结果:
express rest api server
✓ post object (97ms)
✓ retrieves an object
1) retrieves a collection
2) updates an object
3) checks an updated object
4) removes an object
2 passing (179ms)
4 failing
添加 get 获取集合数据
app.get('/collections/:collectionName', function(req, res, next) {
req.collection.find({} ,{limit:10, sort: [['_id',-1]]}).toArray(function(e, results){
if (e) return next(e)
res.send(results)
})
重启、测试,结果:
express rest api server
✓ post object (90ms)
✓ retrieves an object
✓ retrieves a collection
1) updates an object
2) checks an updated object
3) removes an object
3 passing (179ms)
3 failing
添加 update 根据ID修改对象数据
app.put('/collections/:collectionName/:id', function(req, res, next) {
req.collection.updateById(req.params.id, {$set:req.body}, function(e, result){
if (e) return next(e)
res.send(result)
})
重启、测试,结果:
express rest api server
✓ post object (128ms)
✓ retrieves an object
✓ retrieves a collection
✓ updates an object
✓ checks an updated object
1) removes an object
5 passing (230ms)
1 failing
添加 delete 根据ID删除对象
app.del('/collections/:collectionName/:id', function(req, res, next) {
req.collection.removeById(req.params.id, function(e, result){
if (e) return next(e)
res.send((result===1)?{msg:'success'}:{msg:'error'})
})
重启、测试,结果:
express rest api server
✓ post object (40ms)
✓ retrieves an object
✓ retrieves a collection
✓ updates an object
✓ checks an updated object
✓ removes an object
6 passing (103ms)
这样,所有测试都就通过了,代码开发完成
小结
通过这个小例子,可以了解nodejs express的开发方式,并体验了测试驱动的开发方法