docs.nestjs.com icon indicating copy to clipboard operation
docs.nestjs.com copied to clipboard

Documentation examples of transaction support for e2e testing

Open dcs3spp opened this issue 4 years ago • 3 comments

I'm submitting a...


[ ] Regression 
[ ] Bug report
[x] Feature request
[x] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.

Current behavior

Excellent library! I am a new user, possibly, considering using Nestjs with Typeorm. However, I cannot seem to find any official documentation/support for using transactions, especially for the purpose of e2e testing. I have looked and posted on discord with either no response or definitive answers concerning this topic. I have also searched StackOverflow. Other queries relating to transactions, e.g. :

Many responses refer to using Typeorm EntityManager/QueryManager classes since transaction decorators could be possibly be removed in future versions of the Typeorm library. So I gave it a go and have included the code snippet within the Minimal reproduction of the problem with instructions section, below.

Expected behavior

Documentation examples of using transactions with Nestjs/Typeorm for testing purposes.

Minimal reproduction of the problem with instructions

I have included a code snippet attempt below that tries to override the EntityManager provider so that it is initialised with a QueryRunner instance so that I can start and rollback a transaction before/after each test. Not sure if this is the best approach??? This is based on this potential solution, avoiding the use of the @ts-ignore statement. However, I cannot seem to get the repository to use my overridden EntityManager instance, to enable successful transaction usage.....

  let app: INestApplication;
  let testModule: TestingModule;
  
  afterEach(async () => {
    const em: EntityManager = testModule.get(getEntityManagerToken('default'));
    await em.queryRunner.rollbackTransaction();
  });

  beforeEach(async () => {
    const con: Connection = testModule.get(Connection);
    const em: EntityManager = testModule.get(getEntityManagerToken('default'));
    const repo: CourseRepository = testModule.get(CourseRepository);
    const result: boolean = repo.isEntityManagerMine(em); // false => the repo is not using the default entity manager
    const conResult: boolean = repo.isConnectionMine(em.connection); // true => the repo is using the same connection
    await em.queryRunner.startTransaction();
  });

  afterAll(async() => {
    await app.close();
    await testModule.close();
  });

  beforeAll(async () => {
    testModule = await Test.createTestingModule({
      imports: [AppModule],})
    .overrideProvider(getEntityManagerToken('default'))
    .useFactory({
      factory: (connection: Connection): EntityManager => {
        const queryRunner: QueryRunner = connection.createQueryRunner('master');
        const entityManager: EntityManager = connection.createEntityManager(queryRunner);
        return entityManager;
      },
      inject:[getConnectionToken('default')],
    })
    .compile();
  
    app = testModule.createNestApplication();
    await app.init();
  });

 // tests using request from supertest library 

Interestingly, if I do not use overrideproviders in the test module and instead use:

testModule = await Test.createTestingModule({
      imports: [AppModule],
      providers: [
        { 
          provide: getEntityManagerToken('default'),
          useFactory: (connection: Connection) => {
            const queryRunner: QueryRunner = connection.createQueryRunner('master');
            const entityManager: EntityManager = connection.createEntityManager(queryRunner);
            return entityManager;
          },
          inject:[getConnectionToken('default')],
        }
      ],
    })

then the EntityManager instance (within the beforeEach method) is the same as the repository's EntityManager instance. However, the QueryRunner of the EntityManager is undefined, despite initialising it in the createEntityManager method of the connection???

The other alternative, would be dropping the database and recreating before each test, although this might be slower in terms of performance. Another alternative library uses the transactional decorator. However, as mentioned earlier this decorator is scheduled for possible deprecation in future typeorm release.

What is the motivation / use case for changing the behavior?

Facilitate understanding for new users considering using transactions with Nestjs/typeorm for testing purposes.

Environment


Nest version: 6.6.3

 
For Tooling issues:
- Node version: v10.16.0
- Platform: Mac

Others:

dcs3spp avatar Sep 10 '19 13:09 dcs3spp

@kamilmysliwiec Thank you for the great work you're doing with Nestjs. Please an example on how to get this working would be really great. I have been struggling with this also.

fadeojo avatar Jun 01 '20 15:06 fadeojo

@kamilmysliwiec any update on this? I am facing the same issue.

RyanAquino avatar Dec 02 '20 16:12 RyanAquino

Hello, I created a package that emulates a rollback for TypeORM: https://github.com/juanjo96Dev/fastypest

I hope it's useful for you. Greetings!

j-testdev avatar Feb 22 '23 20:02 j-testdev