数据API 数据治理 会员 案例 开发者
掌握聚合最新动态了解行业最新趋势
API接口,开发服务,免费咨询服务
新闻动态 > 媒体报道

谈一谈对Thinkjs3进行业务逻辑测试的方法

引言

在软件开发的过程中,测试对于检验项目的可靠性非常重要。测试的方案有很多,比如手动测试,使用mocha,ava等框架的自动化测试。测试的重点也有不同,有侧重业务逻辑正确性的,有侧重性能的。在Thinkjs3框架下,虽然文档没提,但是官方提供了一种针对model进行的单元测试实践,具体方案在官方Github项目的issue #841中。这种方法需要先在model中写好测试函数,然后通过think.model实例化对象并调用测试函数完成测试。而对于controller层体现的业务逻辑,该方法并不适用。本文介绍一种方法,可以通过在mocha测试框架下模拟http请求的方式去测试业务逻辑,并可以用于travis这样的持续集成中进行自动化测试。

方法

首先在项目根目录下创建testing.js的测试环境文件,内容基本复制production.js:

  • const Application = require('thinkjs');
  • const path = require('path');
  • const instance = new Application({
  • ROOT_PATH: __dirname,
  • proxy: true, // use proxy
  • env: 'testing'
  • });
  • instance.run();
  • instance.runInWorker({ port: 2333 }); #添加这两行
  • module.exports = instance;

然后在test文件夹下,创建一个测试文件:

  • const assert = require('assert');
  • const request = require('supertest');
  • const path = require('path');
  • const instance = require(path.join(process.cwd(), 'testing.js'));
  • describe('Test Title', function() {
  • describe('Test subtitle', function() {
  • it ('Test description', function(done){
  • const f = function() {
  • request(think.app.server).post([url])
  • .set('Content-Type', 'application/json')
  • .send({
  • // ...构造请求参数,supertest具体用法参见supertest文档
  • })
  • .expect(200)
  • .end(function(err, res) {
  • if (err) throw err;
  • // ...测试逻辑
  • done();
  • });
  • };
  • setTimeout(f, 4000);
  • })
  • })
  • after(function () {
  • process.exit();
  • })
  • });
js

并添加adapter.testing.js,在其中声明用于测试环境的配置,比如测试数据库地址,写法同adapter.js,根据官方文档,adapter.js中的同名配置会被覆盖。 最后在package.json中加入test命令: linux环境:

  • "test": "THINK_UNIT_TEST=1 mocha -t 20000"

windows环境:

  • "test": " set THINK_UNIT_TEST=true && mocha -t 20000"

不要忘记安装mocha等相关依赖,在测试前要先npm run compile编译

原理解释

我们要知道为什么这样可以启动一个http server并用于测试服务。


THINK_UNIT_TEST=1如果我们基于原生的koa或者express,那么我们直接用koa()或者express()就能创建出一个http服务。但是按照#841的方法,是不会启动http服务的。原因在于这句环境变量设置。我们追踪testing.js中的instance.run()方法,会定位到thinkjs源码中的lib/application.js文件,会看见根据环境变量与process参数的不同,框架有四条启动分支:

  • run() {
  • if (pm2.isClusterMode) {
  • throw new Error('can not use pm2 cluster mode, please change exec_mode to fork');
  • }
  • // start file watcher
  • if (cluster.isMaster) this.startWatcher();
  • const instance = new ThinkLoader(this.options);
  • const argv = this.parseArgv();
  • try {
  • console.error(argv);
  • if (process.env.THINK_UNIT_TEST) {
  • instance.loadAll('worker', true);
  • } else if (argv.path) {
  • instance.loadAll('worker', true);
  • return this.runInCli(argv);
  • } else if (cluster.isMaster) {
  • instance.loadAll('master');
  • return this.runInMaster(argv);
  • } else {
  • instance.loadAll('worker');
  • return this.runInWorker(argv);
  • }
  • } catch (e) {
  • console.error(e);
  • }
  • }
js

其中,使用了THINK_UNIT_TEST环境会执行try中的第一分支,第一分支如果进一步追踪会发现框架使用thinkLoader加载了所有的配置等,因此think.model可以正常的实例化模型。但是第一分支没有使用return 语句来真正的以某种模式启动服务。在其他三个分支里,框架使用runInCli、runInMaster、runInWorker将服务启动了起来。所以我们需要手动将http服务run起来。

之所以不直接尝试进入其他分支,是因为进入条件不满足。第二第三分支的进入条件是process的argv参数里argv[2]必须存在且为path(第二分支,以命令行方式运行服务)或者port(第三分支)。在平常使用时(npm run start [port]),我们使用node [env].js [port]的方式启动服务,此时port会被正则匹配并解析到argv中,执行第三分支(解析过程具体可以看application.js中的parseArgv()函数)。第二分支我觉得应该是在crontab(定时任务)的场景下使用,被框架自己调用。

而在测试框架中执行时,process的argv[2]中会被注入测试框架自己的参数,比如我们的mocha中,argv[2]='-t',ava中此项会被注入一个json化的对象字符串,但是由于thinkjs没有对path的格式做校验,因此会进入第二分支,并产生错误。


instance.runInWorker({ port: 2333 })使用这句可以手动启动服务,在传入参数中指定port即可。之所以不runInMaster是因为会引发cluster.fork is not a function这个错误,而runInWorker可以直接启动一个工作进程,对于基本的业务逻辑测试已经足够了。这里我们就完成了启动一个http服务。


setTimeout(f, 4000);在测试代码中,我们使用延时任务以等待服务启动的时间。thinkJs提供了'appReady'事件,所以这里可以考虑用think.app.on('appReady')改写。这里如果不做相应的等待,server对象不会被注入到think.app.server中,也就无法使用supertest


process.exit()测试完成后,需要在mocha的after方法里手动关闭测试进程,原因是之前使用runInWorker方法启动的服务仍然在运行。这种方法虽然可以关闭process完成测试,但是Nodejs会残留。不过在travis持续集成下的自动化测试中,最终系统资源都会释放掉。


mocha -t 20000由于mocha默认一个测试应该在两秒内完成,所以需要用-t参数延时以免超时导致测试不通过。

总结

缺点

不能干净的结束掉测试进程,对于本地测试会需要手动关闭node进程

可能的改进思路

在config中,声明一个createServer函数,来自定义启动,以获取supertest所需的server对象。

感谢

@wurining @welefen

原文来自:魔术师的帽子

掌握聚合最新动态了解行业最新趋势
API接口,开发服务,免费咨询服务
新闻动态 > 媒体报道
谈一谈对Thinkjs3进行业务逻辑测试的方法
发布:2018-01-09

引言

在软件开发的过程中,测试对于检验项目的可靠性非常重要。测试的方案有很多,比如手动测试,使用mocha,ava等框架的自动化测试。测试的重点也有不同,有侧重业务逻辑正确性的,有侧重性能的。在Thinkjs3框架下,虽然文档没提,但是官方提供了一种针对model进行的单元测试实践,具体方案在官方Github项目的issue #841中。这种方法需要先在model中写好测试函数,然后通过think.model实例化对象并调用测试函数完成测试。而对于controller层体现的业务逻辑,该方法并不适用。本文介绍一种方法,可以通过在mocha测试框架下模拟http请求的方式去测试业务逻辑,并可以用于travis这样的持续集成中进行自动化测试。

方法

首先在项目根目录下创建testing.js的测试环境文件,内容基本复制production.js:

  • const Application = require('thinkjs');
  • const path = require('path');
  • const instance = new Application({
  • ROOT_PATH: __dirname,
  • proxy: true, // use proxy
  • env: 'testing'
  • });
  • instance.run();
  • instance.runInWorker({ port: 2333 }); #添加这两行
  • module.exports = instance;

然后在test文件夹下,创建一个测试文件:

  • const assert = require('assert');
  • const request = require('supertest');
  • const path = require('path');
  • const instance = require(path.join(process.cwd(), 'testing.js'));
  • describe('Test Title', function() {
  • describe('Test subtitle', function() {
  • it ('Test description', function(done){
  • const f = function() {
  • request(think.app.server).post([url])
  • .set('Content-Type', 'application/json')
  • .send({
  • // ...构造请求参数,supertest具体用法参见supertest文档
  • })
  • .expect(200)
  • .end(function(err, res) {
  • if (err) throw err;
  • // ...测试逻辑
  • done();
  • });
  • };
  • setTimeout(f, 4000);
  • })
  • })
  • after(function () {
  • process.exit();
  • })
  • });
js

并添加adapter.testing.js,在其中声明用于测试环境的配置,比如测试数据库地址,写法同adapter.js,根据官方文档,adapter.js中的同名配置会被覆盖。 最后在package.json中加入test命令: linux环境:

  • "test": "THINK_UNIT_TEST=1 mocha -t 20000"

windows环境:

  • "test": " set THINK_UNIT_TEST=true && mocha -t 20000"

不要忘记安装mocha等相关依赖,在测试前要先npm run compile编译

原理解释

我们要知道为什么这样可以启动一个http server并用于测试服务。


THINK_UNIT_TEST=1如果我们基于原生的koa或者express,那么我们直接用koa()或者express()就能创建出一个http服务。但是按照#841的方法,是不会启动http服务的。原因在于这句环境变量设置。我们追踪testing.js中的instance.run()方法,会定位到thinkjs源码中的lib/application.js文件,会看见根据环境变量与process参数的不同,框架有四条启动分支:

  • run() {
  • if (pm2.isClusterMode) {
  • throw new Error('can not use pm2 cluster mode, please change exec_mode to fork');
  • }
  • // start file watcher
  • if (cluster.isMaster) this.startWatcher();
  • const instance = new ThinkLoader(this.options);
  • const argv = this.parseArgv();
  • try {
  • console.error(argv);
  • if (process.env.THINK_UNIT_TEST) {
  • instance.loadAll('worker', true);
  • } else if (argv.path) {
  • instance.loadAll('worker', true);
  • return this.runInCli(argv);
  • } else if (cluster.isMaster) {
  • instance.loadAll('master');
  • return this.runInMaster(argv);
  • } else {
  • instance.loadAll('worker');
  • return this.runInWorker(argv);
  • }
  • } catch (e) {
  • console.error(e);
  • }
  • }
js

其中,使用了THINK_UNIT_TEST环境会执行try中的第一分支,第一分支如果进一步追踪会发现框架使用thinkLoader加载了所有的配置等,因此think.model可以正常的实例化模型。但是第一分支没有使用return 语句来真正的以某种模式启动服务。在其他三个分支里,框架使用runInCli、runInMaster、runInWorker将服务启动了起来。所以我们需要手动将http服务run起来。

之所以不直接尝试进入其他分支,是因为进入条件不满足。第二第三分支的进入条件是process的argv参数里argv[2]必须存在且为path(第二分支,以命令行方式运行服务)或者port(第三分支)。在平常使用时(npm run start [port]),我们使用node [env].js [port]的方式启动服务,此时port会被正则匹配并解析到argv中,执行第三分支(解析过程具体可以看application.js中的parseArgv()函数)。第二分支我觉得应该是在crontab(定时任务)的场景下使用,被框架自己调用。

而在测试框架中执行时,process的argv[2]中会被注入测试框架自己的参数,比如我们的mocha中,argv[2]='-t',ava中此项会被注入一个json化的对象字符串,但是由于thinkjs没有对path的格式做校验,因此会进入第二分支,并产生错误。


instance.runInWorker({ port: 2333 })使用这句可以手动启动服务,在传入参数中指定port即可。之所以不runInMaster是因为会引发cluster.fork is not a function这个错误,而runInWorker可以直接启动一个工作进程,对于基本的业务逻辑测试已经足够了。这里我们就完成了启动一个http服务。


setTimeout(f, 4000);在测试代码中,我们使用延时任务以等待服务启动的时间。thinkJs提供了'appReady'事件,所以这里可以考虑用think.app.on('appReady')改写。这里如果不做相应的等待,server对象不会被注入到think.app.server中,也就无法使用supertest


process.exit()测试完成后,需要在mocha的after方法里手动关闭测试进程,原因是之前使用runInWorker方法启动的服务仍然在运行。这种方法虽然可以关闭process完成测试,但是Nodejs会残留。不过在travis持续集成下的自动化测试中,最终系统资源都会释放掉。


mocha -t 20000由于mocha默认一个测试应该在两秒内完成,所以需要用-t参数延时以免超时导致测试不通过。

总结

缺点

不能干净的结束掉测试进程,对于本地测试会需要手动关闭node进程

可能的改进思路

在config中,声明一个createServer函数,来自定义启动,以获取supertest所需的server对象。

感谢

@wurining @welefen

原文来自:魔术师的帽子

电话 0512-88869195