I have build a BaseTest from which all my other tests inherit. I am trying to mock the database connection and the database objects within this test. But the test fails to run when I don't have the database up, which tells me it's not mocking it properly.
Here is my BaseTest file:
import * as supertest from 'supertest';
import { ExecutionContext, INestApplication, Type, ValidationPipe } from '@nestjs/common';
import { Test } from '@nestjs/testing';
import { AppModule } from '../app.module';
import { AuthGuard } from '@nestjs/passport';
import { getRepositoryToken } from '@nestjs/typeorm';
import { Account } from '../accounts/entities/account.entity';
import { Session } from '../accounts/entities/session.entity';
import { Repository } from 'typeorm';
import { ApiKey } from '../accounts/entities/apikey.entity';
import { Project } from '../accounts/entities/project.entity';
import { Operator } from '../accounts/entities/operator.entity';
import { ProjectRole } from '../accounts/entities/project-roles.entity';
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
import { GlobalGuard } from '../auth/global.guard';
import { ProjectGuard } from '../auth/project.guard';
import { ModuleRef } from '@nestjs/core';
type MockRepository<T = any> = Partial<Record<keyof Repository<T>, jest.Mock>>;
const createMockRepository = <T = any>(): MockRepository<T> => ({
findOne: jest.fn(),
create: jest.fn(),
save: jest.fn(),
find: jest.fn(),
preload: jest.fn(),
remove: jest.fn(),
});
export abstract class BaseTest {
static app: INestApplication;
static httpServer: any;
static accountRepository: MockRepository;
static projectRepository: MockRepository;
static apikeyRepository: MockRepository;
static operatorRepository: MockRepository;
static projectRoleRepository: MockRepository;
static sessionRepository: MockRepository;
static fakeRequest: any;
static fakeApiRequest: any;
// runs before all tests
static async before(): Promise<void> {
const moduleRef = await Test.createTestingModule({
imports: [AppModule],
})
.overrideGuard(JwtAuthGuard)
.useValue({
canActivate: (ctx: ExecutionContext) => {
const req = ctx.switchToHttp().getRequest();
req.user = { operatorId: 'opid', accountId: '123', email: 'op@gmail.com' };
return true;
},
})
.overrideGuard(GlobalGuard)
.useValue({
canActivate: (ctx: ExecutionContext) => {
return true;
},
})
.overrideGuard(ProjectGuard)
.useValue({
canActivate: (ctx: ExecutionContext) => {
return true;
},
})
.overrideGuard(AuthGuard('api-key'))
.useValue({
canActivate: (ctx: ExecutionContext) => {
const req = ctx.switchToHttp().getRequest();
req['account'] = { id: '123', name: 'test', slug: 'test' };
return true;
},
})
.overrideProvider(getRepositoryToken(Account))
.useValue(createMockRepository())
.overrideProvider(getRepositoryToken(Project))
.useValue(createMockRepository())
.overrideProvider(getRepositoryToken(ApiKey))
.useValue(createMockRepository())
.overrideProvider(getRepositoryToken(Operator))
.useValue(createMockRepository())
.overrideProvider(getRepositoryToken(ProjectRole))
.useValue(createMockRepository())
.overrideProvider(getRepositoryToken(Session))
.useValue(createMockRepository())
.overrideProvider('database')
.useValue({
insert: jest.fn().mockReturnValue({
toPromise: jest.fn().mockResolvedValue(true),
}),
query: jest.fn().mockReturnValue({
toPromise: jest.fn().mockResolvedValue(true),
}),
queryPromise: jest.fn(),
})
.compile();
BaseTest.app = moduleRef.createNestApplication();
BaseTest.app.useGlobalPipes(new ValidationPipe({ transform: true }));
BaseTest.app = await BaseTest.app.init();
BaseTest.httpServer = this.app.getHttpServer();
BaseTest.accountRepository = moduleRef.get<MockRepository>(getRepositoryToken(Account));
BaseTest.projectRepository = moduleRef.get<MockRepository>(getRepositoryToken(Project));
BaseTest.apikeyRepository = moduleRef.get<MockRepository>(getRepositoryToken(ApiKey));
BaseTest.operatorRepository = moduleRef.get<MockRepository>(getRepositoryToken(Operator));
BaseTest.projectRoleRepository = moduleRef.get<MockRepository>(getRepositoryToken(ProjectRole));
BaseTest.sessionRepository = moduleRef.get<MockRepository>(getRepositoryToken(Session));
BaseTest.fakeRequest = { user: { sessionId: 'aaa', operatorId: 'opid', email: 'op@gmail.com', accountId: '123' } };
BaseTest.fakeApiRequest = { projectId: 'projectid' };
}
static async after(): Promise<void> {
expect.hasAssertions();
BaseTest.app = null;
}
get<TInput = any, TResult = TInput>(type: Type<TInput> | string | symbol): TResult {
return BaseTest.app.get(type);
}
server(): supertest.SuperTest<supertest.Test> {
return supertest(BaseTest.httpServer);
}
}
And here is my App.module file:
import { Global, Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UsersModule } from './users/users.module';
import { GroupsModule } from './groups/groups.module';
import { EventsModule } from './events/events.module';
import { AccountsModule } from './accounts/accounts.module';
import { AuthModule } from './auth/auth.module';
import { ConfigModule } from '@nestjs/config';
import { TestdbModule } from './testdb/testdb.module';
import { BullModule } from '@nestjs/bull';
import { ClickHouseModule } from '@depyronick/nestjs-clickhouse';
import { TypeOrmModule } from '@nestjs/typeorm';
import { MailModule } from './mail/mail.module';
import { IntegrationsModule } from './integrations/integrations.module';
@Global()
@Module({
imports: [
UsersModule,
TypeOrmModule.forRoot({
type: 'postgres',
host: process.env.POSTGRES_DATABASE_HOST,
port: parseInt(process.env.POSTGRES_DATABASE_PORT),
username: process.env.POSTGRES_DATABASE_USERNAME,
password: process.env.POSTGRES_DATABASE_PASSWORD,
database: process.env.POSTGRES_DATABASE_NAME,
logging: false,
autoLoadEntities: true,
synchronize: false,
}),
GroupsModule,
EventsModule,
AccountsModule,
ClickHouseModule.register([
{
name: 'database',
host: process.env.CLICKHOUSE_DATABASE_HOST,
database: process.env.CLICKHOUSE_DATABASE_NAME,
username: process.env.CLICKHOUSE_DATABASE_USERNAME,
password: process.env.CLICKHOUSE_DATABASE_PASSWORD,
},
]),
ConfigModule.forRoot({ isGlobal: true }),
BullModule.forRoot({
redis: {
host: process.env.REDIS_HOST,
port: parseInt(process.env.REDIS_PORT),
password: process.env.REDIS_PASSWORD,
},
defaultJobOptions: { removeOnComplete: true },
}),
AuthModule,
TestdbModule,
IntegrationsModule,
MailModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
My guess is that when the basetest imports the app.module, it tries to import the TypeormModule from there and ends up trying to connect to the database. How could I avoid that from happening?