I am trying to test my command handler which has multiple repositories injected into it. When running the test I am getting the following error:
ValidationError: Using global EntityManager instance methods for context specific actions is disallowed. If you need to work with the global instance's identity map, use `allowGlobalContext` configuration option or `fork()` instead.
My DatabaseModule boots up the MikroORM
connection.
I understand that with the app, RequestContext
is created with each request which provides me with a fresh instance of EntityManager
and it is working perfectly fine. But for testing I cannot figure out how to do it.
This is the command handler itself (removed the actual handle function)
@Injectable()
export class RegisterUserCommandHandler
implements ICommandHandler<RegisterUserCommand, { userId: string }>
{
private readonly logger = new Logger(RegisterUserCommandHandler.name);
constructor(
private readonly authorizationService: AuthorizationService,
private readonly userFactory: UserFactory,
private readonly userRepository: UserRepository,
private readonly physicianRepository: PhysicianRepository,
private readonly representativeRepository: LocalRepresentativeRepository,
private readonly pharmacistRepository: PharmacistRepository
) {}
}
This is the test code (I only pasted the relevant section)
import { Test } from "@nestjs/testing";
import { ConfigModule } from "@nestjs/config";
import { EventEmitterModule } from "@nestjs/event-emitter";
import { MikroORM, EntityManager } from "@mikro-orm/core";
import { isLeft, isRight } from "fp-ts/Either";
describe("RegisterUserCommandHandler", () => {
let orm: MikroORM;
let entityManager: EntityManager;
let handler: RegisterUserCommandHandler;
let userFactory: UserFactory;
let userRepository: UserRepository;
let physicianRepository: PhysicianRepository;
let representativeRepository: LocalRepresentativeRepository;
let pharmacistRepository: PharmacistRepository;
beforeEach(async () => {
const moduleRef = await Test.createTestingModule({
imports: [
EventEmitterModule.forRoot(),
ConfigModule.forRoot({
isGlobal: true,
}),
DatabaseModule,
],
providers: [
RegisterUserCommandHandler,
{
provide: AuthorizationService,
useValue: {
isUserAuthorizedAsAdmin: jest.fn(),
},
},
{
provide: PasswordGenerator,
useValue: {
generatePasswordHash: jest.fn(),
validatePassword: jest.fn(),
generateRandomPassword: jest.fn().mockResolvedValue("Password1"),
},
},
UserFactory,
UserRepository,
PhysicianRepository,
LocalRepresentativeRepository,
PharmacistRepository,
],
}).compile();
orm = moduleRef.get<MikroORM>(MikroORM);
handler = moduleRef.get<RegisterUserCommandHandler>(
RegisterUserCommandHandler
);
userFactory = moduleRef.get<UserFactory>(UserFactory);
userRepository = moduleRef.get<UserRepository>(UserRepository);
physicianRepository =
moduleRef.get<PhysicianRepository>(PhysicianRepository);
representativeRepository = moduleRef.get<LocalRepresentativeRepository>(
LocalRepresentativeRepository
);
pharmacistRepository =
moduleRef.get<PharmacistRepository>(PharmacistRepository);
entityManager = orm.em.fork();
});
});
This is the base repository (every repository extends this in the system)
import {
EntityManager,
EntityRepository,
SqlEntityManager,
} from "@mikro-orm/mysql";
import { AnyEntity, EntityName } from "@mikro-orm/core";
import { FindOptions } from "@mikro-orm/core/drivers/IDatabaseDriver";
import { IRepository } from "./repository.interface";
import { SOFT_DELETABLE_FILTER } from "../decorators";
import type { PopulatePath } from "@mikro-orm/core/enums";
export abstract class BaseRepository<T extends AnyEntity<T>>
extends EntityRepository<T>
implements IRepository<T>
{
protected constructor(em: SqlEntityManager, entity: EntityName<T>) {
super(em, entity);
}
//...more code here
}
And finally this is the database module
import { Module } from "@nestjs/common";
import { ConfigModule, ConfigService } from "@nestjs/config";
import { MikroOrmModule } from "@mikro-orm/nestjs";
import { CoreModule, SoftDeletableHandlerSubscriber } from "@riskomed/core";
import { DatabaseConfig } from "./database.config";
import { GlobalSubscriber } from "./global.subscriber";
import mikroOrmConfig from "./mikro-orm.config";
@Module({
imports: [
CoreModule,
ConfigModule.forFeature(DatabaseConfig),
MikroOrmModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (configService: ConfigService) => ({
...mikroOrmConfig(configService),
}),
}),
],
providers: [GlobalSubscriber, SoftDeletableHandlerSubscriber],
})
export class DatabaseModule {}
How can I get past that error without using the allowGlobalContext
?