Niraj Chauhan

Niraj Chauhan

#father #husband #SoftwareCraftsman #foodie #gamer #OnePiece #naruto

NestJS + TypeORM + Postgres InMemory DB E2E testing

Posted by on

NestJS is a great opinionated and battery-included framework. The day I started using NestJS, there was no turning back. It adapts module-oriented design which makes the backend more versatile. With the help of Typescript, it enables dependency injections which makes testing super easy. NestJS integrates easily with major databases like MySQL, Postgres, Mongo, etc. It also comes with a handy CLI tool which makes scaffolding generation very easy. Basically, NestJS comes with a complete ecosystem.

Code in Javascript

Testing is one of the most important and a major part of any application development, NestJS by default uses Jest & Supertest for making unit tests & end-to-end testing jobs easier.

But recently in one of the projects I faced an issue with end-to-end testing. The project was built using NestJS + TypeORM + Postgres. Now with Postgres, there are not many options available for an in-memory database, the one I found pg-mem doesn’t support the latest TypeORM versions. After digging into the error, I got the fix.

The actual issue with pg-mem was that in the latest versions of TypeORM, it uses a Postgres function to make some initial checks, this function was not available with the pg-mem library but the library had an option to write custom functions which solved my issue.

The workaround is really simple, we just need to create a pg-mem connection for TypeORM and replace it with the actual connection. So in our e2e test, the code will be:

describe("PhotoController (e2e)", () => {
  let app: INestApplication;
  let repository: Repository;

  beforeAll(async () => {
    const db = newDb();
    // Register current_database function
    db.public.registerFunction({
      name: "current_database",
      args: [],
      returns: DataType.text,
      implementation: (x) => `hello world ${x}`,
    });

    // Get PG in memory DB connection
    const connection = (await db.adapters.createTypeormConnection({
      type: "postgres",
      entities: [Photo],
      synchronize: true,
    })) as TypeOrmConnectionFactory;

    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [AppModule],
    })
      .overrideProvider("DATABASE_CONNECTION")
      .useValue(connection)
      .compile();

    app = moduleFixture.createNestApplication();

    await app.init();
    repository = app.get > "PHOTO_REPOSITORY";
  });

  afterEach(async () => {
    await repository.query("DELETE from photo;");
  });

  it("/ (GET) records", async () => {
    await repository.save({
      name: "123",
      description: "test",
      filename: "test",
      views: 10,
      data: {
        key: "value",
      },
      isPublished: true,
    });
    return request(app.getHttpServer())
      .get("/photo")
      .expect(200)
      .expect((response: Response) => {
        expect(response.body).toHaveLength(1);
        expect(response.body).toEqual([
          {
            id: 1,
            name: "123",
            description: "test",
            filename: "test",
            views: 10,
            data: { key: "value" },
            isPublished: true,
          },
        ]);
      });
  });
});

We start by registering the missing function in the in-memory DB. This will not give us the error when connecting to the in-memory DB. Then we create a TypeORM connection with the in-memory DB. Finally when we create the TestingModule, here we override the DB provider and this is the key thing that will make connections to in-memory DB instead of the actual one.

You can find the complete source code on my Github repo.