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

如何Mock Nest服务依赖?解决CronJob重复创建测试报错

Nest服务单元测试:解决定时任务冲突与依赖Mock问题

一、先解决「CHECKER_JOB已存在」定时任务冲突

测试时重复初始化服务会导致CronJob重复注册,有两种快速解决方式:

方式1:测试前清除已注册的定时任务

在测试的beforeEach钩子中清空定时任务队列:

import { CronJob } from 'cron';

beforeEach(() => {
  // 停止并移除所有已注册的定时任务
  Object.values(CronJob.scheduledJobs).forEach(job => job.stop());
  Object.keys(CronJob.scheduledJobs).forEach(key => delete CronJob.scheduledJobs[key]);
});

方式2:Mock CronJob类,避免实际注册

直接Mock整个cron模块,让CronJob的实例不执行真实逻辑:

jest.mock('cron', () => {
  const mockCronJob = {
    start: jest.fn(),
    stop: jest.fn()
  };
  return { CronJob: jest.fn(() => mockCronJob) };
});

二、正确Mock所有依赖服务与Repository

针对你的7个服务依赖和1个Repository,提供两种Mock方案:

方案1:手动Mock每个依赖(适合需要自定义Mock行为的场景)

在测试模块中为每个依赖提供Mock实现:

import { Test } from '@nestjs/testing';
import { YourService } from './your.service';
// 导入所有依赖的服务和Repository类
import { DepService1 } from '../dep1/dep1.service';
import { DepService2 } from '../dep2/dep2.service';
// ... 其他5个服务
import { YourRepository } from './your.repository';

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

  beforeEach(async () => {
    const moduleRef = await Test.createTestingModule({
      providers: [
        YourService,
        // 为每个服务依赖提供空Mock
        { provide: DepService1, useValue: jest.fn() },
        { provide: DepService2, useValue: jest.fn() },
        // ... 其他5个服务同理
        // Mock Repository,模拟常用方法
        { provide: YourRepository, useValue: { find: jest.fn(), save: jest.fn() } }
      ],
    }).compile();

    service = moduleRef.get<YourService>(YourService);
  });

  it('should be defined', () => {
    expect(service).toBeDefined();
  });
});

方案2:使用.useMocker批量Mock(解决你之前无效的问题)

如果之前用.useMocker没效果,大概率是没正确匹配依赖的token。正确用法如下:

import { Test } from '@nestjs/testing';
import { YourService } from './your.service';
import { getRepositoryToken } from '@nestjs/typeorm'; // 若用TypeORM的Repository
import { YourRepository } from './your.repository';

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

  beforeEach(async () => {
    const moduleRef = await Test.createTestingModule({
      providers: [YourService],
    })
    .useMocker(token => {
      // 单独处理Repository的token(TypeORM的Repository token不是类本身)
      if (token === getRepositoryToken(YourRepository)) {
        return { find: jest.fn(), save: jest.fn() };
      }
      // 匹配所有服务类依赖,返回空Mock
      if (typeof token === 'function') {
        return jest.fn();
      }
    })
    .compile();

    service = moduleRef.get<YourService>(YourService);
  });

  it('should be defined', () => {
    expect(service).toBeDefined();
  });
});

如果你的Repository是自定义的(非TypeORM),直接匹配Repository类即可,无需getRepositoryToken。

三、验证Jest配置的快速技巧

若仅需确认Jest配置正常,可添加一个基础测试:

it('should run basic jest test', () => {
  expect(1 + 1).toBe(2);
});

此测试通过则说明Jest配置无问题,后续只需聚焦Nest测试模块的配置。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 18:50:04