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

chai.request接口测试中Mock失效问题求助

解决Chai Request中Mock函数不生效的问题

你的问题核心是Stub只对测试用例里手动创建的ScheduleRepository实例生效,但API路由里的实例没有使用到Stub后的原型,通常是因为路由里的ScheduleRepository实例化时机和Stub时机不匹配导致的。下面是几种可行的修复方案:

1. 调整路由实例化方式,确保每次请求创建新实例

如果你的路由文件里是在模块加载时就提前实例化了ScheduleRepository(比如const repo = new ScheduleRepository()放在路由顶部),那你在测试里Stub的原型不会影响这个已经创建好的实例——原型Stub只对Stub之后创建的实例生效。

修改路由代码,改为每次请求时才创建实例:

// 路由文件示例
app.get('/schedule', (req, res) => {
  // 每次请求都新建实例,这样Stub的原型会作用到这个实例上
  const scheduleRepo = new ScheduleRepository();
  // 后续调用scheduleRepo的getStudentLessonPlan等方法
});

2. 使用模块级Stub(推荐,更灵活)

如果不想修改路由代码,可以用proxyquire替换整个ScheduleRepository模块的导出,这样不管路由里怎么实例化,都会用你的Stub对象。

安装proxyquire:

npm install proxyquire --save-dev

修改测试代码:

import chai from 'chai';
import chaiHttp from 'chai-http';
import sinon from 'sinon';
import proxyquire from 'proxyquire';

chai.use(chaiHttp);

describe("Schedule API Routes", () => {
    let scheduleRepoStub: {
        getStudentLessonPlan: sinon.SinonStub;
        getAccountIdAndSessionStatus: sinon.SinonStub;
    };
    let app: any;

    beforeEach(() => {
        // 创建包含Mock方法的Stub对象
        scheduleRepoStub = {
            getStudentLessonPlan: sinon.stub().resolves([]),
            getAccountIdAndSessionStatus: sinon.stub().resolves([{
                isExpired: true,
                accountType: "STUDENT",
                accountId: 123,
            }])
        };

        // 用proxyquire加载app,替换ScheduleRepository为我们的Stub
        // 注意替换成你实际的文件路径
        app = proxyquire('../path/to/your/app', {
            './path/to/ScheduleRepository': scheduleRepoStub
        });
    });

    it("Should return 200 - student schedule", (done) => {
        chai.request(app)
            .get("/schedule?from=2023-05-22&to=2023-05-27")
            .set("Cookie", "test=x5rup8koxga2lr53e7uljg5ifasapa3go1jgl2g55huu4v0ihq8an1ueavb8rxh")
            .set("user-type", "student")
            .end((err, res) => {
                res.body.should.be.an("object");
                res.should.have.status(200);
                
                // 验证Stub方法是否被调用,确认Mock生效
                sinon.assert.calledOnce(scheduleRepoStub.getAccountIdAndSessionStatus);
                sinon.assert.calledOnce(scheduleRepoStub.getStudentLessonPlan);
                
                done();
            });
    });
});

3. 调整Stub和App加载顺序

如果路由里的实例是在模块加载时创建的,那要先Stub原型,再加载app,这样路由里的实例会继承Stub后的原型方法:

import chai from 'chai';
import chaiHttp from 'chai-http';
import sinon from 'sinon';
import ScheduleRepository from '../path/to/ScheduleRepository';

chai.use(chaiHttp);

describe("Schedule API Routes", () => {
    let scheduleRepositoryStubForLessonPlan: sinon.SinonStub;
    let scheduleRepositoryStubForSession: sinon.SinonStub;
    let app: any;

    beforeEach(() => {
        // 先Stub原型方法
        scheduleRepositoryStubForLessonPlan = sinon.stub(ScheduleRepository.prototype, "getStudentLessonPlan").resolves([]);
        scheduleRepositoryStubForSession = sinon.stub(ScheduleRepository.prototype, "getAccountIdAndSessionStatus").resolves([{
            isExpired: true,
            accountType: "STUDENT",
            accountId: 123,
        }]);

        // 再加载app,确保路由里的实例使用Stub后的原型
        app = require('../path/to/your/app');
    });

    afterEach(() => {
        // 恢复所有Stub
        sinon.restore();
    });

    it("Should return 200 - student schedule", (done) => {
        chai.request(app)
            .get("/schedule?from=2023-05-22&to=2023-05-27")
            .set("Cookie", "test=x5rup8koxga2lr53e7uljg5ifasapa3go1jgl2g55huu4v0ihq8an1ueavb8rxh")
            .set("user-type", "student")
            .end((err, res) => {
                res.body.should.be.an("object");
                res.should.have.status(200);
                
                // 验证Stub被调用
                sinon.assert.calledOnce(scheduleRepositoryStubForSession);
                
                done();
            });
    });
});

验证技巧

每次测试后添加Stub调用断言(比如sinon.assert.calledOnce),可以快速确认Mock是否真的生效,避免因路由逻辑问题导致的误判。

内容的提问来源于stack exchange,提问作者Michał

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 09:49:57