行为驱动开发:英文缩写BDD,详细介绍可参见这里。
测试驱动开发
以前,我一般用TDD(测试驱动开发)来做服务器端,以确保开发质量,以及提高开发速度。
很多人的体会是写测试用例是延缓了开发进度。也许是开发场景不一样,或者是没有熟练的使用测试驱动开发的技巧。
先写测试用例,然后写实现代码,完成一点功能,就运行测试用例,多次测试的情况下(回归测试),所花时间肯定比每次都手工测试来的少。
这里的关键是,首先要写测试代码,再写正式代码,这样正式代码的每次测试都使用测试程序来测试。
我怎么看BDD
我也不知道,老实话,我也才用这个东西。我是想找一个nodejs环境下比较普及的测试框架,结果看到Mocha,它的测试可以引用BDD概念,我才决定试一试的。
也许,用一段时间,我会发现它确实比TDD好的地方。
在在nodejs应用中抛出异常只是给出了实现的设想。
在这里:https://github.com/MarshalW/HelloExpress/tree/v0.1 ,给出了具体的实现。
见lib/db目录下的两个代码:
- Config.js ,用于配置db模块的参数等;
- DocProvider.js,实现对mongodb的dao封装,示意性的实现,只实现了getCollection和insert
在app.js最后部分,是对该模块的调用:
var book={name:’枪炮、病毒和钢铁’};var docProvider=new DocProvider(‘books’);docProvider.insert(book,{},function(result){console.log(‘insert ok.’);});
日志引用
如果写测试代码,就离不开断言,assertion。或者,也有人在正式代码中使用断言,来减少因为接受不正确的值产生的错误。
在nodejs中能找到assertion模块。
写法类似这样:
var assert=require(‘assert’);
assert.equal(1,2);
equal接收两个参数,如果相等,就过去,否则抛出异常,类似这样:
AssertionError: 1 == 2
at Object.<anonymous> (/Users/marshal/WebstormProjects/HelloExpress/app.js:66:8)
在javascript通过闭包回调是非常容易的。下面一段代码来自正式项目中,基于express,操作mongodb:
DataProvider.prototype.getCollection = function (callback) {
this.db.collection(this.collectionName, function (err, collection) {
if (err) callback(err);
在操作mongodb中,类似的代码很多。问题在于,callback函数负担很重,既要执行业务正常逻辑,又要处理是否有错误。这让程序员编码负担很重,往往不编写对错误的处理。这样一旦有错误,程序还继续执行业务逻辑,带来混乱。
其实,这里一旦有错误,业务逻辑也是无法继续执行的。那么,直接抛异常吧。参见在javascript中使用异常。
可写成类似这样:
DataProvider.prototype.getCollection = function (callback) {
this.db.collection(this.collectionName, function (err, collection) {
if (err) throw(err);
callback(collection);
这样,调用该方法的写法是:
DataProvider.prototype.update = function (selector, document, options, callback) {
this.getCollection(function (collection) {
collection.update(selector, document, options, function(err, result) {
if (err) throw(err);
callback(result);
});
});
这样就可不必在每个callback中写重复的劳什子错误处理了。
日志引用
引发异常很简单:
throw “Hello error”;
捕获异常的语法也和java差不多:
function test() {
throw “Test error”;
}try {
test();
} catch (err) {
console.log(err);
}finally{
console.log(‘finally’);
}
日志引用
nodejs,服务器端的javascript,也需要通过继承来实现代码的复用。
下面写的语法,未依赖nodejs函数,应该是标准的javascript语法。
var Hello = function (name) {
this.name = name;
Hello.prototype.sayHello = function () {
console.log(‘hello, ‘ + this.name);
};
};var hello = new Hello(‘zhangsan’);
hello.sayHello();var HelloImpl = function (name, title) {
this.title = title;
Hello.call(this, name);HelloImpl.prototype.sayHello=function(){
console.log(‘Title: ‘+this.title);
Hello.prototype.sayHello.call(this);
};
};HelloImpl.prototype = new Hello();
var helloImpl = new HelloImpl(‘zhangsan’, ‘Mr.’);
helloImpl.sayHello();
默认情况下,socket.io会打印大量的日志,比如这样:
debug: client authorized
info: handshake authorized 949572516804683503
debug: setting request GET /socket.io/1/websocket/949572516804683503
debug: set heartbeat interval for client 949572516804683503
debug: client authorized for
debug: websocket writing 1::
可以通过设置日志级别,屏蔽掉这些debug或者info的日志。
比如这样:
var express = require(‘express’)
, routes = require(‘./routes’)
, io = require(‘socket.io’);var app = module.exports = express.createServer(),
io = io.listen(app);// Configuration
io.set(‘log level’, 0);
详细socket.io配置参见:https://github.com/LearnBoost/Socket.IO/wiki/Configuring-Socket.IO
另外,日志级别的设置见:https://github.com/LearnBoost/Socket.IO/blob/master/lib/logger.js
设置的log level的数字,其实就是日志数组下标。
用图书的示例来说明一下mongodb命令的使用。
添加
插入一个图书文档:
db.books.insert({name:’深入学习MongoDB’});
然后,可以看一下是否插入成功了:
db.books.find();
打印的结果类似这样:
{ “_id” : ObjectId(“4f8e8a8e7a919fd8a1a37e2d”), “name” : “深入学习MongoDB” }
让nodejs应用后台执行,最简单的办法是:
nohup node your_app.js &
但是,forever能做更多的事情,比如分别记录输出和错误日志,比如可以在js中作为api使用。
我们只是很简单的在命令行里使用它。
安装很简单:
sudo npm install forever -g
使用forever启动守护进程:
forever start server.js
关闭守护进程:
forever stop server.js
如果需要记录输出日志和错误:
forever start -l forever.log -o out.log -e err.log server.js
现在项目中使用nodejs,代码是堆出来了,但是复用性很差,以后维护成问题。
通过以下方式,做了个复用的方案。
常量
如果一遍一遍的写下面这样的代码:
var bookProvider = new BookProvider(“localhost”, 27017);
这部分代码,应该用常量来解决,nodejs提供了Globals模块,里面的global可以设置常量:
global.mongodbHost=’localhost’;
global.mongodbPort=27017;
使用:
console.log(‘mongodb host:’+global.mongodbHost);