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

MERN项目POST方法单元测试出现‘For async tests and hooks, ensure "done()" is called’错误的排查与解决

解决你的MERN项目POST接口单元测试超时问题

嘿,我看了你的代码和错误信息,这个超时问题主要是几个明显的代码错误导致的,咱们一步步来修复:

核心错误原因分析

1. POST路由处理函数参数顺序完全颠倒(最关键原因)

在routes/products.js的POST路由里,你把Express路由的参数顺序写反了:

// ❌ 错误写法:把err放在第一个参数位
router.post("/product/save", (err, req, res, next) => { ... })

Express路由处理函数的正确参数顺序是(req, res, next),你把错误对象放在首位,导致这个路由永远无法正确接收请求参数、返回响应——请求发出去后一直挂起,最终触发测试超时。

2. 测试文件使用了路由实例而非完整Express应用

你的测试文件post.js里直接导入了路由文件:

const app = require("../../../routes/products.js");

但supertest需要的是完整的Express应用实例,而非单独的路由模块,这会导致测试请求没有正确的应用上下文。

3. 测试断言的拼写错误

测试里的porperty是拼写错误,正确应为property——虽然这不是超时的直接原因,但会导致后续断言失败。


一步步修复步骤

步骤1:修正POST路由的参数顺序

修改routes/products.js里的POST路由,同时建议返回创建的产品数据,方便测试断言:

// ✅ 正确写法
router.post("/product/save", (req, res) => {
  let newProduct = new Products(req.body);
  newProduct.save((err) => {
    if (err) {
      return res.status(400).json({ 
        error: err,
      });
    }
    return res.status(200).json({ 
      success: "Product saved successfully",
      product: newProduct // 返回创建的产品,方便测试验证字段
    });
  });
});

步骤2:在server.js中导出Express应用实例

修改server.js,添加app的导出,让测试能使用完整的应用:

// ... 其他代码 ...
app.listen(PORT, () => {
  console.log(`App is running on ${PORT}`);
});

// 新增app的导出
module.exports = { connect, close, app };

步骤3:修正测试文件的应用导入和断言拼写

修改test/api/products/post.js,同时添加错误捕获避免超时:

const expect = require("chai").expect;
const request = require("supertest");
// ✅ 改为导入server.js里的完整app和连接方法
const { app, conn } = require("../../../server.js"); 

describe("POST /products", () => {
  before((done) => {
    conn
      .connect()
      .then(() => done())
      .catch((err) => done(err));
  });
  after((done) => {
    conn
      .close()
      .then(() => done())
      .catch((err) => done(err));
  });
  it("OK, creating a new product works", (done) => {
    request(app)
      .post("/product/save")
      .send({ 
        productName: "Mango", 
        description: "description Mango", 
        price: "rs 50", 
        productCategory: "fruits", 
        productUrl: "https://upload.wikimedia.org/wikipedia/commons/9/90/Hapus_Mango.jpg"
      })
      .then((res) => {
        const body = res.body.product; // 对应路由返回的product字段
        // ✅ 修正拼写错误porperty → property
        expect(body).to.contain.property("_id"); 
        expect(body).to.contain.property("productName");
        expect(body).to.contain.property("description");
        expect(body).to.contain.property("price");
        expect(body).to.contain.property("productCategory");
        expect(body).to.contain.property("productUrl");
        done();
      })
      // ✅ 新增catch捕获请求错误,避免测试因未处理的错误超时
      .catch(err => done(err)); 
  });
});

可选优化:用async/await简化测试

你可以改用async/await替代done回调,代码更简洁,也不容易遗漏done调用:

it("OK, creating a new product works", async () => {
  const res = await request(app)
    .post("/product/save")
    .send({ 
      productName: "Mango", 
      description: "description Mango", 
      price: "rs 50", 
      productCategory: "fruits", 
      productUrl: "https://upload.wikimedia.org/wikipedia/commons/9/90/Hapus_Mango.jpg"
    });
  
  const body = res.body.product;
  expect(body).to.contain.property("_id");
  expect(body.productName).to.equal("Mango");
  // 其他断言...
});

完成这些修改后,你的测试应该就能正常运行了——路由能正确处理POST请求并返回响应,测试也能拿到响应进行断言,不会再出现超时问题。

内容的提问来源于stack exchange,提问作者Nimasha Fernando

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 16:47:34