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

使用Jest测试路由控制器:如何断言response.redirect已被调用?

在Jest中Mock HTTP响应对象并断言redirect调用

当然可以!其实在Jest里Mock HTTP响应对象(包括redirect这类核心方法)是测试路由控制器的常规操作,我给你分享几个实用的方案,帮你搞定断言问题:

方案1:手动创建Mock Response对象

这是最直接灵活的方式——既然我们只需要验证redirect的调用情况,完全不需要真实的HTTP响应实例,直接手动构建一个带mock函数的响应对象就行。

举个实际例子:
假设你的控制器代码是这样的:

// controllers/authController.js
exports.handleLogin = (req, res) => {
  // 模拟登录逻辑
  if (req.body.username === 'admin') {
    res.redirect('/admin/dashboard');
  } else {
    res.redirect('/user/home');
  }
};

对应的测试代码可以这么写:

// controllers/__tests__/authController.test.js
const authController = require('../authController');

describe('handleLogin controller', () => {
  test('should redirect to admin dashboard when username is admin', () => {
    // 构建mock的请求和响应对象
    const mockReq = { body: { username: 'admin' } };
    const mockRes = {
      redirect: jest.fn() // 把redirect替换成Jest的mock函数,方便追踪调用
    };

    // 执行控制器方法
    authController.handleLogin(mockReq, mockRes);

    // 断言redirect的调用情况
    expect(mockRes.redirect).toHaveBeenCalledTimes(1);
    expect(mockRes.redirect).toHaveBeenCalledWith('/admin/dashboard');
  });

  test('should redirect to user home for non-admin users', () => {
    const mockReq = { body: { username: 'guest' } };
    const mockRes = { redirect: jest.fn() };

    authController.handleLogin(mockReq, mockRes);

    expect(mockRes.redirect).toHaveBeenCalledWith('/user/home');
  });
});

方案2:复用Mock工具函数

如果多个测试文件都需要用到mock的req/res对象,可以把它抽成工具函数,避免重复代码:

// tests/utils/mockHttp.js
// 创建mock请求对象
exports.createMockReq = (overrides = {}) => ({
  body: {},
  params: {},
  query: {},
  ...overrides
});

// 创建mock响应对象
exports.createMockRes = () => ({
  redirect: jest.fn(),
  send: jest.fn(),
  json: jest.fn(),
  status: jest.fn().mockReturnThis() // 链式调用的方法要mock返回自身
});

然后在测试里直接用:

const { createMockReq, createMockRes } = require('../../utils/mockHttp');

test('example test', () => {
  const mockReq = createMockReq({ body: { email: 'test@example.com' } });
  const mockRes = createMockRes();

  // ...执行控制器逻辑
  expect(mockRes.redirect).toHaveBeenCalled();
});

方案3:全局Mock基础HTTP响应模型(谨慎使用)

如果你想全局Mock Node.js的基础http.ServerResponse类(也就是你说的“基础HTTP响应模型”),也可以用jest.mock来实现,但这种方式要谨慎,因为会影响所有测试:

// 在jest.setup.js(需要在jest.config.js里配置setupFilesAfterEnv)中添加:
jest.mock('http', () => {
  const originalHttp = jest.requireActual('http');
  return {
    ...originalHttp,
    ServerResponse: jest.fn().mockImplementation(() => ({
      redirect: jest.fn(),
      // 按需添加其他你需要Mock的方法
    }))
  };
});

不过一般不推荐这种全局Mock,因为不同测试场景可能需要不同的响应行为,局部手动Mock的可控性更强。

为什么之前的尝试没生效?

核心原因是:真实的HTTP响应对象(不管是框架创建的还是Node.js原生的)的redirect方法不是Jest的mock函数,Jest无法追踪它的调用情况。只有把redirect替换成jest.fn()创建的mock函数,才能用toHaveBeenCalled这类断言方法验证。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 07:48:59