nestjs / docs.nestjs.com

The official documentation https://docs.nestjs.com 📕
MIT License
1.18k stars 1.69k forks source link

Documentation examples of transaction support for e2e testing #666

Open dcs3spp opened 5 years ago

dcs3spp commented 5 years ago

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:

fadeojo commented 4 years ago

@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.

RyanAquino commented 3 years ago

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

j-testdev commented 1 year ago

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

I hope it's useful for you. Greetings!