Closed matinjugou closed 6 years ago
初步判断如下: ../../base.js问题是组内同学写错了 之所以server不存在是因为在环境变量THINK_UNIT_TEST=1的情况下框架没有runserver,但是去掉该环境变量并使用mocha测试后,由于process.argv[2]不存在,框架会从第四分支使用runInworker启动服务,使用think.app.on('appReady')监听服务后,在travis的持续集成自动化测试中程序会卡死,不知道为什么 testing.js
const Application = require('thinkjs');
const path = require('path');
const instance = new Application({
ROOT_PATH: __dirname,
proxy: true, // use proxy
env: 'testing'
});
instance.run();
测试代码
const assert = require('assert');
const request = require('supertest');
const path = require('path');
require(path.join(process.cwd(), 'testing.js'));
think.app.on('appReady', () => {
console.error('appReady');
console.error('server=', think.app.server);
if (think.app.server !== undefined) {
describe('staff', function() {
describe('login', function() {
it ('server should run and login should failed', function(){
/*
request(think.app.server).post('/api/staff/login')
.set('Content-Type', 'application/json')
.send({
staffId: '1_s1',
password: '1_s2'
})
.expect('Content-Type', /json/)
.expect(200)
.end(function(err, res) {
if (err) throw err;
console.error("res=", res);
});
*/
const a = think.model('staff');
console.error(a);
assert.equal(1,2);
done();
})
})
});
}
});
问题已解决,摸索出了一个测试实践
博客地址:http://blog.magichc7.com/post/thinkjs3-functiontest.html
首先在项目根目录下创建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();
})
});
并添加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);
}
}
其中,使用了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
npm WARN thinkjs-application@1.0.0 No repository field.请问一下这个错误怎么解决呀
@lyy666-git 这是一个 warning 忽略即可,具体错误内容是说你的项目没有在 package.json 中配置 git 仓库地址。
DESC
按照issue #841 的方法进行测试的时候,想用supertest模拟请求进行API测试,然后发现虽然think.model有内容,但是think.app却没有按照运行流程所说注入think.app.server,也就没办法构造http请求进行测试。以为是异步的问题,但是使用繁忙等待发现think.app.server一直是undefined。另外,按照这样的方式进行测试,travis也出现了报错,如果把require(path.join(process.cwd(), 'production.js'));换成require(path.join(process.cwd(), 'development.js'));,就不会有
这样的报错,如果直接把production.js里面的代码放进test的文件里(路径深度增加了一层),就不会有这样的报错
但是会提示think.model undefined 求解到底怎么用supertest进行测试?
ENV
OS Platform: travis.ci
Node.js Version: 9.3.0
ThinkJS Version:3.2.3
code
production.js
development.js
error message
more description
// your detail description