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
2 Answers
Reset to default 20Okay, 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
};
},
}),
]
})