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

如何在Express API测试中Mock基于ioredis的Redis ES6类?

Mock Redis集群类进行Express API测试(Jest/TS-Jest + Supertest)

下面分两种常用方案解决你替换ioredis为mock版本的问题,直接匹配你的开发场景:

方案1:手动Mock RedisClusterClient类(灵活控制方法行为)

如果你的Redis集群类是单独模块导出的(比如src/redis/RedisClusterClient.ts),直接用Jest的模块Mock功能替换整个类,自定义方法的返回逻辑。

假设你的Redis集群类结构:

// src/redis/RedisClusterClient.ts
import Redis from 'ioredis';

export class RedisClusterClient {
  private cluster: Redis.Cluster;

  constructor(nodes: Redis.ClusterNode[]) {
    this.cluster = new Redis.Cluster(nodes);
  }

  async get(key: string): Promise<string | null> {
    return this.cluster.get(key);
  }

  async set(key: string, value: string): Promise<string> {
    return this.cluster.set(key, value);
  }
}

测试文件实现:

// src/routes/user.test.ts
import request from 'supertest';
import express from 'express';
import userRouter from './user';
import { RedisClusterClient } from '../redis/RedisClusterClient';

// Mock整个RedisClusterClient模块
jest.mock('../redis/RedisClusterClient', () => ({
  RedisClusterClient: jest.fn().mockImplementation(() => ({
    get: jest.fn(),
    set: jest.fn(),
  })),
}));

const app = express();
app.use(express.json());
app.use(userRouter);

describe('GET /users/:id', () => {
  let mockRedisClient: jest.Mocked<RedisClusterClient>;

  // 每次测试前重置mock,避免测试污染
  beforeEach(() => {
    (RedisClusterClient as jest.Mock).mockClear();
    mockRedisClient = (RedisClusterClient as jest.Mock).mock.instances[0] as jest.Mocked<RedisClusterClient>;
  });

  it('返回200和用户数据当用户存在时', async () => {
    const mockUser = { id: '123', name: 'Chris' };
    // 模拟get方法返回序列化后的用户数据
    mockRedisClient.get.mockResolvedValue(JSON.stringify(mockUser));

    const res = await request(app).get('/users/123');

    expect(res.status).toBe(200);
    expect(res.body).toEqual(mockUser);
    expect(mockRedisClient.get).toHaveBeenCalledExactlyOnceWith('user:123');
  });

  it('返回404当用户不存在时', async () => {
    mockRedisClient.get.mockResolvedValue(null);

    const res = await request(app).get('/users/456');

    expect(res.status).toBe(404);
    expect(res.body.message).toBe('User not found');
  });
});

方案2:用ioredis-mock替换真实ioredis(模拟真实Redis行为)

如果需要模拟Redis的真实操作逻辑(比如自动处理过期、哈希结构等),可以用ioredis-mock直接替换ioredis模块。

第一步:安装依赖

npm install --save-dev ioredis-mock

测试文件实现:

// src/routes/user.test.ts
import request from 'supertest';
import express from 'express';
import userRouter from './user';
import Redis from 'ioredis';
import RedisMock from 'ioredis-mock';

// Mock ioredis,用ioredis-mock的Cluster替换真实Cluster
jest.mock('ioredis', () => {
  const original = jest.requireActual('ioredis');
  return {
    ...original,
    Cluster: RedisMock.Cluster,
  };
});

const app = express();
app.use(express.json());
app.use(userRouter);

describe('GET /users/:id', () => {
  let mockCluster: Redis.Cluster;

  beforeEach(async () => {
    // 初始化mock集群并清空数据
    mockCluster = new Redis.Cluster([{ host: 'localhost', port: 6379 }]);
    await mockCluster.flushall();
  });

  it('返回200和用户数据当用户存在时', async () => {
    const mockUser = { id: '123', name: 'Chris' };
    // 先往mock集群写入数据
    await mockCluster.set('user:123', JSON.stringify(mockUser));

    const res = await request(app).get('/users/123');

    expect(res.status).toBe(200);
    expect(res.body).toEqual(mockUser);
  });

  it('返回404当用户不存在时', async () => {
    const res = await request(app).get('/users/456');

    expect(res.status).toBe(404);
    expect(res.body.message).toBe('User not found');
  });
});

关键注意点

  • 若你的Redis客户端是通过依赖注入(比如放在Express的req.app.locals或用IoC容器),测试时直接传入mock实例即可,无需Mock模块,这种方式耦合度更低。
  • TypeScript环境下,用jest.Mocked<T>来包装mock对象的类型,避免类型报错。
  • 务必在beforeEach里重置mock或清空mock数据,防止测试用例之间互相干扰。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 22:23:37