软件的结构与可测试性关心密切,这不仅是指大粒度的软件结构,如模块化、接口等等,也包括很多细节,如环境变量与参数传递等。
环境变量以很多种方式存在,包括配置文件中的配置、Session中的变量、process.env以及保存在数据库等持久化机制中的变量,环境变量可以直接访问,或者通过在模块或函数中直接读取获得,比如下面的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| const config = require('config'); var MongoClient = require('mongodb').MongoClient;
const mongosettingsurl = process.env.mongo_url || config.mongo.url const mongosettingsdb = process.env.mongo_db || config.mongo.db const LeaveApply = require("../domain/leaveApply")
class MongoRepository { constructor() {
}
//保存请假申请 async save(leaveApply) { var mongodb = await MongoClient.connect(mongosettingsurl); var dbase = mongodb.db(mongosettingsdb);
|
在模块代码中直接读取环境变量,这样做的问题是,当我们需要对这个模块进行测试时,需要使用环境变量传递测试数据库的地址:
1 2 3 4 5 6 7 8 9
| before(async function () { mongoServer = await MongoMemoryServer.create(); const mongoUri = mongoServer.getUri(); process.env.mongo_url=mongoUri process.env.mongo_db="testdb" const Repository=require('../../modules/repositories/mongoRepository') repository=new Repository() service=new LeaveApplyService(repository) })
|
注意上面Repository的引入必须在process.env.mongo_url和 process.env.mongo_db的定义之后,不能在程序开始引入,这是因为模块引入时对内部变量进行了赋值:
1 2
| const mongosettingsurl = process.env.mongo_url || config.mongo.url const mongosettingsdb = process.env.mongo_db || config.mongo.db
|
如果需要mongosettingsurl使用process.env.mongo_url的值,process.env.mongo_url必须在这段代码之前赋值。
这种结构是很脆弱的,编写测试的人员必须充分了解mongoRepository的代码结构,否则无法进行测试。这还是在我们可以方便地重写process.env的情况下,如果只是从配置文件或数据库中直接读取,那么这段代码可能就无法测试。
因此,这种结构必须进行修改,方法很简单,将参数从构造函数注入:
1 2 3 4 5 6 7
| class MongoRepository { constructor(mongosettingsurl,mongosettingsdb) { this.mongosettingsurl=mongosettingsurl this.mongosettingsdb=mongosettingsdb this.collectionName="leave_apply" }
|
这样,MongoRepository不需要知道环境变量的存在,也不关心参数从何处而来,在测试时,也不需要修改的值,测试代码如下:
1 2 3 4 5 6 7
| before(async function () { mongoServer = await MongoMemoryServer.create(); const mongoUri = mongoServer.getUri(); const Repository=require('../../modules/repositories/mongoRepository') repository=new Repository(mongoUri,"testdb") service=new LeaveApplyService(repository) })
|
代码中对Repository引入位置就不重要了,可以放在前面,因为潜在的耦合关系已经被去掉了。