最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Using test database when e2e-testing NestJS - Stack Overflow

programmeradmin0浏览0评论

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],
    })pile();

    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)
      pile();

    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.

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.
Share Improve this question edited Feb 14, 2021 at 9:48 Roy Ra asked Feb 14, 2021 at 8:53 Roy RaRoy Ra 5942 gold badges8 silver badges28 bronze badges
Add a comment  | 

2 Answers 2

Reset to default 20

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');
  });
});

For those looking for setup e2e tests that hit endpoints and assert the response body, you can do something like this:

// app.module.ts
@Module({
  imports: [
    TypeOrmModule.forRootAsync({
      useFactory: async (configService: ConfigService) => {
        if (process.env.APPLICATION_ENV === 'test') {
          return {
            type: 'sqlite',
            database: ':memory:',
            entities: [Entity],
            synchronize: true,
          }
        }
        return {
          // your default options
        };
      },
    }),
  ]
})
发布评论

评论列表(0)

  1. 暂无评论