Jest单元测试中,日志请求体到数据库的中间件如何放置done()?
问题:Connect中间件异步日志的测试困境
一、自定义日志中间件代码(logger.js)
const connect = require('connect'); module.exports = function(db_con){ const app = connect(); app.use((req,res,next)=>{ const sql = `INSERT INTO requests VALUES (:method,:url) returning id`; const stmt = db.prepare(initialInsert); const insert_result = initialInsertStmt.run({method:req.method,'url':req.url }); req.id = insert_result.lastInsertRowid; next(); }); app.use((req,res,next)=>{ var body = []; req.on('data',(data)=> body.push(data)); req.on('end',() => { body = body.toString(); const sql = `UPDATE requests SET body=:body) where id=:id`; const stmt = db.prepare(initialInsert); const insert_result = initialInsertStmt.run({'body':body,'id':req.id}); next(); }); }
二、中间件使用方式
const connect = require('connect'); const logger = require('./logger.js'); const sqlite3 = require('better-sqlite3'); const app = connect(); app.use(logger(sqlite3('./access_log.sqlite'))); app.use((req,res,next)=> res.end('hello')); app.listen(8080);
三、测试代码与核心问题
我编写了测试代码,需要验证中间件的两个核心功能:
- 将插入数据库的ID赋值到
req.id - 在发送响应前把完整请求数据保存到数据库
测试代码如下:
const logger = require('./logger.js'); const request = require('supertest'); const sqlite3 = require('better-sqlite3'); test('post Is logged into DB',(done)=>{ const db = sqlite3(':memory:'); const app = connect(); app.use(logger(db)); app.use((req,res,next())=>{ expect(req.id).notToBe(null); const sql = `SElECT * from requests where id=:id` const result = db.prepare(sql); // 断言逻辑写在这里 }); app.use((req,res)=>res.end("hello")); request(app) .post('/example?param1=11¶m2=22') .set('Host','example.com') .set('X-MyHeader',3) .set("Cookie", ['c_param1=12345667', 'c_param2=blah']) .send("username=techbos&password=Pa%24%24w0rd") .then(()=>{}); })
当前的核心困境是异步日志操作导致测试结束标记done()的位置无法确定:
- 若将
done()放在测试用中间件内,测试可能挂起,因为后续的请求处理代码仍在执行 - 若将
done()放在supertest的then回调内,测试可能在响应发送、日志写入完成前就提前结束
两种错误尝试的代码示例
- 把
done()放在测试中间件的情况:
app.use((req,res,next)=>{ expect(req.id).notToBe(null); const sql = `SElECT * from requests where id=:id` const result = db.prepare(sql); // 断言逻辑写在这里 done(); });
测试会因后续代码未执行完毕而挂起。
- 把
done()放在supertest的then回调的情况:
request(app) .post('/example?param1=11¶m2=22') .set('Host','example.com') .set('X-MyHeader',3) .set("Cookie", ['c_param1=12345667', 'c_param2=blah']) .send("username=techbos&password=Pa%24%24w0rd") .then(()=>{ done() });
测试可能在app.use((req,res)=>res.end("hello"));执行前就提前结束。
另外,我还想了解是否可以通过手动模拟HTTP请求的方式来执行断言逻辑。
内容的提问来源于stack exchange,提问作者Dimitrios Desyllas
相关产品推荐
相关产品推荐

