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

TypeScript类构造器注入Redis客户端遇类型错误,求Mock测试方案

TypeScript下强类型Redis使用方案(支持Mock单元测试)

问题根源

你遇到的类型不匹配错误,本质是直接依赖RedisClientType具体类型时,版本差异或导入方式导致的类型定义冲突。解决核心是依赖抽象而非具体实现,既解决类型问题,又能轻松Mock客户端做单元测试。


步骤1:抽象Redis操作接口

先定义Service实际需要的Redis操作接口,避免直接绑定Redis官方类型:

// redis.ts
import { createClient, type RedisClientType } from 'redis';
import config from '../../config';

// 定义Service依赖的缓存操作接口(按需添加方法)
export interface ICacheClient {
  get(key: string): Promise<string | null>;
  set(key: string, value: string, options?: { EX?: number }): Promise<'OK'>;
  del(key: string): Promise<number>;
}

// 初始化Redis客户端(处理异步连接)
let redisClient: RedisClientType;

async function initRedisClient() {
  if (!redisClient) {
    redisClient = createClient({ url: config.redis_url });
    redisClient.on('error', err => console.error('Redis连接错误:', err));
    await redisClient.connect();
  }
  return redisClient;
}

// 导出适配接口的客户端实例(确保连接完成)
export async function getCacheClient(): Promise<ICacheClient> {
  const client = await initRedisClient();
  return {
    get: key => client.get(key),
    set: (key, value, options) => client.set(key, value, options),
    del: key => client.del(key),
  };
}

步骤2:Service依赖抽象接口

让Service依赖自定义的ICacheClient接口,而非Redis官方类型:

// MyService.ts
import type { ICacheClient } from './redis';

export default class MyService {
  private cacheClient: ICacheClient;

  constructor(cacheClient: ICacheClient) {
    this.cacheClient = cacheClient;
  }

  // 示例业务方法
  async getCachedValue(key: string): Promise<string | null> {
    return this.cacheClient.get(key);
  }

  async setCachedValue(key: string, value: string, ttl?: number): Promise<void> {
    await this.cacheClient.set(key, value, ttl ? { EX: ttl } : undefined);
  }
}

步骤3:业务代码中使用

确保Redis连接完成后再初始化Service:

// 使用示例
import { getCacheClient } from './redis';
import MyService from './MyService';

async function bootstrap() {
  const cacheClient = await getCacheClient();
  const myService = new MyService(cacheClient);
  
  // 调用业务方法
  await myService.setCachedValue('user:1', '{"name":"John"}', 3600);
  const user = await myService.getCachedValue('user:1');
  console.log(user);
}

bootstrap();

步骤4:单元测试Mock客户端

用Jest为例,直接MockICacheClient接口即可,无需依赖真实Redis:

// MyService.test.ts
import MyService from './MyService';

// Mock缓存客户端
const mockCacheClient = {
  get: jest.fn().mockResolvedValue('{"name":"MockedJohn"}'),
  set: jest.fn().mockResolvedValue('OK'),
  del: jest.fn().mockResolvedValue(1),
};

describe('MyService', () => {
  let service: MyService;

  beforeEach(() => {
    jest.clearAllMocks();
    service = new MyService(mockCacheClient);
  });

  test('getCachedValue应返回Mock值', async () => {
    const result = await service.getCachedValue('user:1');
    expect(result).toBe('{"name":"MockedJohn"}');
    expect(mockCacheClient.get).toHaveBeenCalledWith('user:1');
  });

  test('setCachedValue应正确调用set方法', async () => {
    await service.setCachedValue('user:2', '{"name":"Jane"}', 1800);
    expect(mockCacheClient.set).toHaveBeenCalledWith('user:2', '{"name":"Jane"}', { EX: 1800 });
  });
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 08:57:37