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

NestJS单元测试中如何Mock MikroORM的wrap().assign()方法

解决MikroORM单元测试中wrap(user).assign报错的问题

问题场景

业务中使用MikroORM实现用户更新逻辑,核心代码如下:

const user = await this.userRepository.findOneOrFail({ id });
wrap(user).assign(userData);
await this.userRepository.flush();
return await this.readById(user.id);

该逻辑与MikroORM官方NestJS示例项目中的用户更新实现类似,但官方示例未覆盖该更新方法的单元测试。编写单元测试时出现以下报错:

(0 , core_1.wrap)(...).assign is not a function
TypeError: (0 , core_1.wrap)(...).assign is not a function
    at UserService.updateUser (C:\Users\DanielMendesSechisnk\Projects\nestjs-rest-api\src\users\user.service.ts:37:16)
    at processTicksAndRejections (node:internal/process/task_queues:95:5)
    at Object.<anonymous> (C:\Users\DanielMendesSechisnk\Projects\nestjs-rest-api\src\users\tests\user.service.spec.ts:89:12)

报错原因

MikroORM的wrap函数仅对实体代理对象有效:当传入的是MikroORM通过EntityManager创建的实体代理时,wrap会返回包含assign方法的EntityWrapper实例;若单元测试中userRepository.findOneOrFail返回的是普通JS字面量对象,wrap无法识别为实体,返回的包装器无assign方法,从而触发报错。

解决方案

方案1:使用EntityManager创建实体代理(推荐)

在测试中通过MikroORM的EntityManager创建实体代理,而非普通对象,确保wrap能正常识别:

import { Test, TestingModule } from '@nestjs/testing';
import { UserService } from '../user.service';
import { getRepositoryToken } from '@mikro-orm/nestjs';
import { User } from '../entities/user.entity';
import { EntityManager } from '@mikro-orm/core';

describe('UserService', () => {
  let service: UserService;
  let userRepository: jest.Mocked<typeof User>;
  let em: EntityManager;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        UserService,
        {
          provide: getRepositoryToken(User),
          useValue: {
            findOneOrFail: jest.fn(),
            flush: jest.fn(),
          },
        },
        // 注入真实的EntityManager(或Mock核心方法)
        { provide: EntityManager, useValue: jest.requireActual('@mikro-orm/core').EntityManager },
      ],
    }).compile();

    service = module.get<UserService>(UserService);
    userRepository = module.get(getRepositoryToken(User));
    em = module.get<EntityManager>(EntityManager);
  });

  it('should update user successfully', async () => {
    // 创建实体代理对象
    const mockUser = em.create(User, { id: 1, username: 'old_user', email: 'old@example.com' });
    userRepository.findOneOrFail.mockResolvedValue(mockUser);

    const updateData = { username: 'new_user' };
    await service.updateUser(1, updateData);

    expect(userRepository.findOneOrFail).toHaveBeenCalledWith({ id: 1 });
    expect(userRepository.flush).toHaveBeenCalled();
    // 验证属性已更新
    expect(mockUser.username).toBe('new_user');
  });
});

方案2:Mock wrap函数的返回值

若不想引入EntityManager,可直接在测试中Mockwrap函数,使其返回包含assign方法的对象:

// 在测试文件顶部Mock wrap函数
jest.mock('@mikro-orm/core', () => {
  const original = jest.requireActual('@mikro-orm/core');
  return {
    ...original,
    wrap: jest.fn((entity) => ({
      // 模拟assign行为,直接合并数据到实体
      assign: (data) => Object.assign(entity, data),
    })),
  };
});

import { Test, TestingModule } from '@nestjs/testing';
import { UserService } from '../user.service';
import { getRepositoryToken } from '@mikro-orm/nestjs';
import { User } from '../entities/user.entity';

describe('UserService', () => {
  let service: UserService;
  let userRepository: jest.Mocked<typeof User>;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        UserService,
        {
          provide: getRepositoryToken(User),
          useValue: {
            findOneOrFail: jest.fn().mockResolvedValue({ id: 1, username: 'old_user' }),
            flush: jest.fn(),
          },
        },
      ],
    }).compile();

    service = module.get<UserService>(UserService);
    userRepository = module.get(getRepositoryToken(User));
  });

  it('should update user successfully', async () => {
    const updateData = { username: 'new_user' };
    await service.updateUser(1, updateData);

    expect(userRepository.findOneOrFail).toHaveBeenCalledWith({ id: 1 });
    expect(userRepository.flush).toHaveBeenCalled();
  });
});

方案3:调整业务代码(可选,不推荐)

若无需依赖MikroORM的实体代理特性,可直接用Object.assign替代wrap(user).assign,但此方法会跳过MikroORM的字段验证、关系处理等逻辑:

// 修改业务代码
const user = await this.userRepository.findOneOrFail({ id });
Object.assign(user, userData);
await this.userRepository.flush();
return await this.readById(user.id);

内容的提问来源于stack exchange,提问作者Daniel Mendes Sechinske

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 08:01:33