如何测试接收参数的Express请求体校验中间件?
如何测试带参数的Express中间件?
原中间件代码
我实现了一个用来校验请求体键的Express中间件:
function check(expectedKeys: string[], req: Request): boolean{ if (expectedKeys.length !== Object.keys(req.body).length) return false; for (const key of expectedKeys) { if (!(key in req.body)) return false; } return true; } export default function checkRequestBodyKeys(expectedKeys: string[]) { return (req: Request, res: Response, next: NextFunction) => { const isValid = check(expectedKeys, req); if (isValid) return next(); return res.status(Status.BadRequest).json({status: Status.BadRequest, error: ErrorMessage.InvalidRequestBody}); } }
路由调用方式
在路由中我这样使用它:
import { Router } from "express"; import postAuth from "../controllers/auth.controller"; import checkRequestBodyKeys from "../middlewares/checkRequestBodyKeys.middleware" export const authRoute = Router(); authRoute.post("/", checkRequestBodyKeys(["email", "password"]), postAuth);
测试遇到的问题
我希望测试这个中间件:验证它在请求体符合要求时调用next(),不符合时返回400错误响应。但这类接收参数的中间件不知道怎么测试,我写的测试代码明显有问题:
describe("validateRequestBody middleware", () => { let mockRequest: Partial<Request>; let mockResponse: Partial<Response>; let nextFunction: NextFunction = jest.fn(); beforeEach(() => { mockRequest = {}; mockResponse = { status: jest.fn().mockReturnThis(), json: jest.fn(), }; }); test('short name should return error', async () => { const expectedResponse = [{error: "Invalid name"}]; mockRequest = { body: { name: "aa", email: "test@yahoo.com", password: "###!!!AAAbbb111222" } } const check = checkRequestBodyKeys( ["name", "email", "password"] ); expect( checkRequestBodyKeys(["name", "email", "password"]) ).toEqual(Function) }); });
正确的测试实现
核心逻辑是:先调用checkRequestBodyKeys(预期键数组)拿到真正的中间件函数,再传入mock的req、res、next执行,最后断言对应行为是否符合预期。
以下是完整的测试代码:
import { Request, Response, NextFunction } from 'express'; import checkRequestBodyKeys from '../middlewares/checkRequestBodyKeys.middleware'; // 假设Status和ErrorMessage是你定义的常量,需要导入或者直接用对应值 import { Status, ErrorMessage } from '../constants'; describe("checkRequestBodyKeys middleware", () => { let mockRequest: Partial<Request>; let mockResponse: Partial<Response>; let nextFunction: NextFunction; beforeEach(() => { // 重置mock,避免用例间状态污染 mockRequest = {}; mockResponse = { status: jest.fn().mockReturnThis(), json: jest.fn(), }; nextFunction = jest.fn(); }); test('请求体包含所有指定键且无多余键时,调用next', () => { mockRequest.body = { email: 'test@example.com', password: '123456' }; // 生成目标中间件函数 const middleware = checkRequestBodyKeys(['email', 'password']); // 执行中间件 middleware(mockRequest as Request, mockResponse as Response, nextFunction); // 断言行为符合预期 expect(nextFunction).toHaveBeenCalledTimes(1); expect(mockResponse.status).not.toHaveBeenCalled(); expect(mockResponse.json).not.toHaveBeenCalled(); }); test('请求体缺少指定键时,返回400错误', () => { mockRequest.body = { email: 'test@example.com' }; const middleware = checkRequestBodyKeys(['email', 'password']); middleware(mockRequest as Request, mockResponse as Response, nextFunction); expect(nextFunction).not.toHaveBeenCalled(); expect(mockResponse.status).toHaveBeenCalledWith(Status.BadRequest); expect(mockResponse.json).toHaveBeenCalledWith({ status: Status.BadRequest, error: ErrorMessage.InvalidRequestBody }); }); test('请求体有多余键时,返回400错误', () => { mockRequest.body = { email: 'test@example.com', password: '123456', nickname: 'test' }; const middleware = checkRequestBodyKeys(['email', 'password']); middleware(mockRequest as Request, mockResponse as Response, nextFunction); expect(nextFunction).not.toHaveBeenCalled(); expect(mockResponse.status).toHaveBeenCalledWith(Status.BadRequest); expect(mockResponse.json).toHaveBeenCalledWith({ status: Status.BadRequest, error: ErrorMessage.InvalidRequestBody }); }); });
关键说明
- 每次测试前重置mock实例,避免不同测试用例间的状态干扰
- 先通过
checkRequestBodyKeys(keys)生成具体的中间件函数,再传入mock对象执行 - 根据不同测试场景,分别断言
next是否被调用,或者res.status、res.json的调用参数是否符合预期
内容的提问来源于stack exchange,提问作者Guilherme Massoqueto
相关产品推荐
相关产品推荐

