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
相关产品推荐
相关产品推荐

