Poang 是一个基于 express framework 部署在 Heroku / MongoLab 上的 Node.js 应用,该应用的作者 Stephen Bronstein 详细描述了如何写单元测试、功能测试、集成测试并在云端使用 Strider 做持续集成开发。
Poang 主要业务逻辑是使用 Everyauth 做本地身份认证,并将用户身份信息通过 Mongoose-Auth 持久化到 MongoDB 中去 ( Mongoose 做对象建模),利用 Connect-Mongo 做会话保存。 Poang 中的认证代码 auth.js 大部分取自 Mongoose-Auth docs 。
测试代码使用了多种技术框架, Mocha 做单元测试, should 做断言, Sinon.JS 监测以及制造 mock 数据, Zombie.js 做基于浏览器的轻量级集成测试。
单元测试
写一个基于 web/ 数据库的单元测试比较麻烦,有很多配置项需要添加,这里举一个简单例子来阐述如何使用 Mocha 框架做单元测试
describe(‘index’, function() {
describe(’#timesTwo()’, function() {
it(‘should multiply by two’, function() {
var x = 5;
var xTimesTwo = index.timesTwo(x);
xTimesTwo.should.equal(10);
});
});
});
注:前三行表述了单元测试的目标类和目标函数 (一个乘二函数),单元测试的代码体输入参数为 5,利用 should 断言来校验返回值是否为 10
功能测试
1) Case1
创建 mock 请求对象 mock_req,来验证服务端的身份认证功能。使用 Sinon.JS 的 spy 功能监测 mock 响应对象 mock_res 是否有做重定向,如果没有做重定向,则将 mock 响应对象的 http 状态码设置为 200
var mock_req = {session: {}};
var mock_res = {redirect: function() {}, end: function() {}};
sinon.spy(mock_res,“redirect”);
middleware.require_auth_browser(mock_req, mock_res, function() {
mock_res.statusCode = 200;
});
服务端会验证 mock 请求对象是否包含用户信息,因为我们 mock 请求不包含用户对象,所以服务端会返回 http 401 状态码 (未授权),并跳转到 login 页面
mock_res.statusCode.should.eql(401);
mock_res.redirect.getCall(0).args[0].should.equal(’/login’);
2) Case2
重新创建一个 mock 对象,包含用户信息,此时我们断定,mock 响应对象的状态码被设置为 200
mock_req = {user: {}};
middleware.require_auth_browser(mock_req, mock_res, function() {
mock_res.statusCode = 200;
});
mock_res.statusCode.should.eql(200);
集成测试
接下来使用 Zombie.js 做轻量级基于浏览器的集成测试,步骤如下:
首先启动 Poang 应用实例,随机选择一个服务端口号 (当然也可以自己指定一个端口号,但要保证该端口号没有被占用)
var TEST_PORT = Math.floor(Math.random()*61439 + 4096);
before(function() {
var server = app.init(config);
// should check to see if something is listening on the port first
server.listen(TEST_PORT);
console.log(‘Server is listening on port %s’, TEST_PORT);
});
确认下这个服务是可用的,并且页面的标题是”Poang”
var browser = new zombie();
browser.visit(base_url, function () {
browser.success.should.be.ok;
if (browser.error) {
console.dir(‘errors reported:’, browser.errors);
}
done();
});
browser.text(“title”).should.eql(“Poang”);
注册一个用户,校验注册流程是否成功
var browser = new zombie();
browser.visit(base_url + “register”, function () {
browser.query("#register").should.be.ok;
// Fill email, password and submit form
browser.fill(“email”, test_email).fill(“password”, “secret”).
pressButton(“register”, function() {
// Form submitted, new page loaded.
browser.success.should.be.ok;
browser.location.pathname.should.eql("/");
done();
});
});
因为每次集成测试,都会注册同样的帐号,为了避免困扰,我们在跑完集成测试的时候可以将帐号从数据库中删掉 (这个取决于测试的具体策略,可以灵活调整)
after(function(done) {
var db_uri = process.env.MONGOLAB_URI || process.env.MONGODB_URI || config.default_db_uri;
mongoose.connect(db_uri);
// drop database
mongoose.connection.db.executeDbCommand( {dropDatabase:1}, function() {
console.log(“Dropped test database”);
done();
});
})
Strider 持续集成
Strider 是一个基于 Node.js 和 Python 的持续集成平台, Strider 本身可以集成任意的测试框架,唯一的要求是应用必须支持”NPM Test”命令,而 Strider 对测试用具 TAP 有很好的集成性,所以 Poang 应用的描述文件 Package.json 会添加如下命令:
“test”: “mocha -R tap --globals o,section,data,m,k,i”
“–globals o,section,data,m,k,I”指定全局变量,不然会报全局变量遗漏错误
以下是开发部署应用过程中,总结的最佳实践
MongoDB 的配置
启动 MongDB 的时候,需要关闭数据库文件大小的预分配,不然会间或出现单元测试连接数据库超时
mongod --noprealloc --nojournal
Heroku/MongoLab 上部署 Poang 应用
- 创建进程文件 Procfile,并指定如何启动该进程,详细可以参照 Heroku 指南
- web: node app.js
- MongoLab 数据库连接的环境变量
- 在 Heroku 上添加 MongoLab 模块插件,使用 Heroku Toolbelt 创建 MongoLab 模块,命令如下 (如果在本地目录,无需指定应用名称)
heroku addons:add mongolab:starter --app [your_app_name]
感兴趣的读者,具体可以在 Github 参照 Poang 项目源代码以及 Stephen Bronstein 的技术博客。
评论