如何测试Express.js ErrorHandler?求助TypeScript+Sinon函数失败案例测试方法
测试
findById函数的失败场景(TypeScript + Sinon) 你的findById函数失败场景核心是:当findByPk返回null时,抛出NotFound错误。用Sinon可以轻松模拟findByPk的返回值,结合断言库验证错误是否正确抛出。
测试步骤与代码示例
假设你使用Mocha作为测试框架、Chai(搭配chai-as-promised)作为断言库,示例代码如下:
import { expect } from 'chai'; import sinon from 'sinon'; import { UserService } from './user.service'; // 替换为你的服务类路径 import { NotFound } from './errors'; // 替换为你的错误类路径 import { UserModel } from './user.model'; // 替换为你的模型类路径 describe('UserService.findById', () => { let userService: UserService; let findByPkStub: sinon.SinonStub; beforeEach(() => { // 模拟UserModel的findByPk方法 findByPkStub = sinon.stub(UserModel, 'findByPk'); // 初始化服务实例(如果服务依赖模型,按需注入) userService = new UserService(); }); afterEach(() => { // 恢复被stub的方法,避免影响其他测试 findByPkStub.restore(); }); it('抛出NotFound错误当用户不存在时', async () => { // 让findByPk返回null findByPkStub.resolves(null); // 验证异步函数是否抛出指定错误 await expect(userService.findById(999)).to.be.rejectedWith(NotFound, 'User not found'); // 验证stub被正确调用 expect(findByPkStub.calledOnceWith(999)).to.be.true; }); });
核心要点
- 用
sinon.stub替换模型的findByPk方法,通过resolves(null)模拟异步返回空值 - 使用
chai-as-promised的rejectedWith断言异步函数的错误类型与消息 - 验证stub的调用情况,确保代码走对了分支
测试Express.js的错误处理器
Express错误处理器是接收err, req, res, next四个参数的中间件,测试时需要模拟请求、响应对象,验证处理器对不同错误的处理逻辑。
先假设你的错误处理器实现
// error-handler.ts import { Request, Response, NextFunction } from 'express'; import { NotFound } from './errors'; export const errorHandler = (err: Error, req: Request, res: Response, next: NextFunction) => { if (err instanceof NotFound) { return res.status(404).json({ message: err.message }); } // 兜底处理未知错误 res.status(500).json({ message: 'Internal server error' }); };
测试代码示例
import { expect } from 'chai'; import sinon from 'sinon'; import { errorHandler } from './error-handler'; import { NotFound } from './errors'; import { Request, Response } from 'express'; describe('errorHandler', () => { let req: Partial<Request>; let res: Partial<Response>; let statusStub: sinon.SinonStub; let jsonStub: sinon.SinonStub; beforeEach(() => { req = {}; // 模拟res的链式调用:res.status().json() jsonStub = sinon.stub().returnsThis(); statusStub = sinon.stub().returns({ json: jsonStub } as Partial<Response>); res = { status: statusStub }; }); it('处理NotFound错误:返回404状态码与对应消息', () => { const notFoundErr = new NotFound('User not found'); errorHandler(notFoundErr, req as Request, res as Response, sinon.spy()); expect(statusStub.calledOnceWith(404)).to.be.true; expect(jsonStub.calledOnceWith({ message: 'User not found' })).to.be.true; }); it('处理未知错误:返回500状态码与通用消息', () => { const genericErr = new Error('Unexpected error'); errorHandler(genericErr, req as Request, res as Response, sinon.spy()); expect(statusStub.calledOnceWith(500)).to.be.true; expect(jsonStub.calledOnceWith({ message: 'Internal server error' })).to.be.true; }); });
核心要点
- 用Sinon模拟
res.status和res.json方法,通过链式stub实现Express的调用逻辑(status返回res自身,所以让stub返回包含jsonstub的对象) - 针对不同错误类型分别测试,验证状态码和返回的JSON内容是否符合预期
- 无需调用
next,错误处理器是请求链的最后一步
内容的提问来源于stack exchange,提问作者Vinicius Bortoletto
相关产品推荐
相关产品推荐

