penetrator.go
penetrator.go

Reputation: 31

How can I use @Transactional Decorator In Nest.js

I want ask about my Issue with Testing @Transactional Decorator in NestJS/MikroORM Environment.

I'm encountering an issue while testing a command handler that uses MikroORM's @Transactional decorator. While the code works properly in production, I'm getting the following error during testing:

Error: expect(received).rejects.toThrow(expected)
Expected constructor: OrganizationNameAlreadyRegisteredException
Received constructor: Error
Received message: "@Transactional() decorator can only be applied to methods of classes with `orm: MikroORM` property, `em: EntityManager` property, or with a callback parameter like `@Transactional(() => orm)` that returns one of those types. The parameter will contain a reference to current `this`. Returning an EntityRepository from it is also supported."

Here's my command handler:

import { EntityManager } from '@mikro-orm/postgresql';

@CommandHandler(OrganizationRegisterCommand)
export class OrganizationRegisterCommandHandler {
  constructor(
    @InjectRepository(Organization)
    private readonly organizationRepository: OrganizationRepository,
    @Inject(EntityManager)
    private readonly em: EntityManager,
  ) {}

  @Transactional()
  async execute(command: OrganizationRegisterCommand): Promise<void> {
    try {
      const organization = await Organization.register({ ...command });
      await this.em.persistAndFlush(organization);
      // await this.organizationRepository.persist(organization);
    } catch (error) {
      if (error instanceof UniqueConstraintViolationException) {
        logger.log('OrganizationNameAlreadyRegisteredException', error);
        throw new OrganizationNameAlreadyRegisteredException();
      }
      console.log('etc error', error);
    }
  }
}

My module configuration:

@Module({
  imports: [CqrsModule, MikroOrmModule.forFeature([Organization])],
  providers: [
    OrganizationEditCommandHandler,
    OrganizationRemoveCommandHandler,
    {
      provide: OrganizationRegisterCommandHandler,
      useFactory: (em: EntityManager, organizationRepository: EntityRepository<Organization>) => {
        return new OrganizationRegisterCommandHandler(organizationRepository, em);
      },
      inject: [EntityManager, EntityRepository],
    },
    OrganizationRepository,
  ],
  controllers: [OrganizationController],
})
export class OrganizationModule {}

And here's my test code:

describe('usecase', () => {
  let handler: OrganizationRegisterCommandHandler;
  let organizationRepository: jest.Mocked<OrganizationRepository>;
  let entityManager: EntityManager;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        OrganizationRegisterCommandHandler,
        {
          provide: getRepositoryToken(Organization),
          useValue: {
            findOne: jest.fn(),
            persist: jest.fn(),
            flush: jest.fn(),
            selectOrganizationBy: jest.fn(),
          },
        },
        {
          provide: EntityManager,
          useValue: {
            persistAndFlush: jest.fn(),
            flush: jest.fn(),
          },
        },
      ],
    }).compile();

    handler = module.get<OrganizationRegisterCommandHandler>(OrganizationRegisterCommandHandler);
    organizationRepository = module.get(getRepositoryToken(Organization));
    entityManager = module.get(EntityManager);
  });
});

I've injected EntityManager into the handler and applied the @Transactional decorator to the execute method. However, while this works fine in the actual application, it fails during testing. I would greatly appreciate any advice on how to properly test code that uses the @Transactional decorator with MikroORM in a NestJS environment.

Upvotes: 0

Views: 52

Answers (0)

Related Questions