Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using test database when e2e-testing NestJS

In this project, it uses NestJS along with TypeORM. For real API requests, CRUD operation is being operated on MySQL(which is using AWS RDS).

Now I am trying to use SQLite(In-Memory) to test API results.

I successfully implemented this in Unit Test, as the code below.

First, below is create-memory-db.ts, which returns a connection to in-memory SQLite database.

type Entity = Function | string | EntitySchema<any>;

export async function createMemoryDB(entities: Entity[]) {
  return createConnection({
    type: 'sqlite',
    database: ':memory:',
    entities,
    logging: false,
    synchronize: true,
  });
}
  • And by using the exported function above, I successfully ran Unit test, like below.
describe('UserService Logic Test', () => {
  let userService: UserService;
  let connection: Connection;
  let userRepository: Repository<User>;

  beforeAll(async () => {
    connection = await createMemoryDB([User]);
    userRepository = await connection.getRepository(User);
    userService = new UserService(userRepository);
  });

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

  afterEach(async () => {
    await userRepository.query('DELETE FROM users');
  });

  // testing codes.
});

I am trying to do the same thing on e2e tests. I tried below code.

// user.e2e-spec.ts

describe('UserController (e2e)', () => {
  let userController: UserController;
  let userService: UserService;
  let userRepository: Repository<User>;
  let connection: Connection;
  let app: INestApplication;
  const NAME = 'NAME';
  const EMAIL = '[email protected]';
  const PASSWORD = '12345asbcd';

  beforeAll(async () => {
    connection = await createMemoryDB([User]);
    userRepository = await connection.getRepository(User);
    userService = new UserService(userRepository);
    userController = new UserController(userService);

    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [],
      controllers: [UserController],
      providers: [UserService],
    }).compile();

    app = moduleFixture.createNestApplication();
    await app.init();
  });

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

  afterEach(async () => {
    // await userRepository.query('DELETE FROM users');
  });

  it('[POST] /user : Response is OK if conditions are right', () => {
    const dto = new UserCreateDto();
    dto.name = NAME;
    dto.email = EMAIL;
    dto.password = PASSWORD;

    return request(app.getHttpServer())
      .post('/user')
      .send(JSON.stringify(dto))
      .expect(HttpStatus.CREATED);
  });
});

I cannot create UserModule since it doesn't have a constructor with Connection parameter. The code itself has no compile error, but gets results below when e2e test is executed.

Nest can't resolve dependencies of the UserService (?). Please make sure that the argument UserRepository at index[0] is available in the RootTestModule context.

Potential solutions:
- If UserRepository is a provider, is it part of the current RootTestModule?
- If UserRepository is exported from a seperate @Module, is that module imported within RootTestModule?
  @Module({
    imports: [/* The module containing UserRepository */]
  })


TypeError: Cannot read property 'getHttpServer' of undefined.

Any help would be greatly appreciated. Thanks :)


  • UPDATE : New error occured after trying below.
describe('UserController (e2e)', () => {
  let userService: UserService;
  let userRepository: Repository<User>;
  let connection: Connection;
  let app: INestApplication;
  const NAME = 'NAME';
  const EMAIL = '[email protected]';
  const PASSWORD = '12345asbcd';

  beforeAll(async () => {
    connection = await createMemoryDB([User]);
    userRepository = await connection.getRepository(User);
    userService = new UserService(userRepository);

    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [UserModule],
    })
      .overrideProvider(UserService)
      .useClass(userService)
      .compile();

    app = moduleFixture.createNestApplication();
    await app.init();
  });

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

  afterEach(async () => {
    await userRepository.query('DELETE FROM users');
  });

  it('[POST] /user : Response is OK if conditions are right', async () => {
    const dto = new UserCreateDto();
    dto.name = NAME;
    dto.email = EMAIL;
    dto.password = PASSWORD;

    const result = await request(app.getHttpServer())
      .post('/user')
      .send(JSON.stringify(dto))
      .expect({ status: HttpStatus.CREATED });
  });
});
  • I checked if query is working, and was able to see that it is using SQLite database as I wanted. But new error appeared in console.
TypeError: metatype is not a constructor.

TypeError: Cannot read property 'getHttpServer' of undefined.
like image 903
Roy Ra Avatar asked Feb 21 '26 19:02

Roy Ra


1 Answers

Okay, I solved this issue by using TypeOrm.forRoot() inside the imports field of Test.createTestingModule. Below is how I did it.

describe('UserController (e2e)', () => {
  let userService: UserService;
  let userRepository: Repository<User>;
  let app: INestApplication;
  const NAME = 'NAME';
  const EMAIL = '[email protected]';
  const PASSWORD = '12345asbcd';

  beforeAll(async () => {
    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [
        UserModule,
        TypeOrmModule.forRoot({
          type: 'sqlite',
          database: ':memory:',
          entities: [User],
          logging: true,
          synchronize: true,
        }),
      ],
    }).compile();

    app = moduleFixture.createNestApplication();
    await app.init();
    userRepository = moduleFixture.get('UserRepository');
    userService = new UserService(userRepository);
  });

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

  afterEach(async () => {
    await userRepository.query('DELETE FROM users');
  });
});

like image 180
Roy Ra Avatar answered Feb 23 '26 09:02

Roy Ra



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!