You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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&param2=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回调内,测试可能在响应发送、日志写入完成前就提前结束

两种错误尝试的代码示例

  1. 把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();
});

测试会因后续代码未执行完毕而挂起。

  1. 把done()放在supertest的then回调的情况:
request(app)
      .post('/example?param1=11&param2=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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 21:10:33