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

如何测试接收参数的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 19:07:39