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ł
相关产品推荐
相关产品推荐

