ConfigService is undefined while running testcases in Nest.js - jestjs

I am running nestjs server locally and if I test with postman it works fine. But when I run testcases, the ConfigService gets undefined!
app.module.ts
#Module({
imports: [
ConfigModule.forRoot({ load: [configuration], isGlobal: true }),
AppDeployModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
configuration.ts
export default () => ({
app: {
baseUrl: 'https://app-deploy.com',
},
});
app-deploy.controller.spec.ts
describe('AppDeployController', () => {
let controller: AppDeployController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [AppDeployController],
providers: [AppDeployService],
imports: [ConfigModule],
}).compile();
controller = module.get<AppDeployController>(AppDeployController);
});
it.only('should create an app', async () => {
const res = await controller.create({ appName: 'Hello World' });
console.log(res);
});
});
Here in service file, configService.get('app') is undefined!
app-deploy.service.ts
#Injectable()
export class AppDeployService {
constructor(private configService: ConfigService) {}
create(createAppDeployDto: CreateAppDeployDto) {
console.log(this.configService.get('app')); // Here while running test it gets undefined
const baseUrl = this.configService.get('app').baseUrl;
return {
appName: createAppDeployDto.appName,
appUrl:
baseUrl +
'/' +
createAppDeployDto.appName.toLowerCase().replace(' ', '-'),
};
}
}
This is the result after running test.
FAIL src/app-deploy/app-deploy.controller.spec.ts
AppDeployController
× should create an app (51 ms)
● AppDeployController › should create an app
TypeError: Cannot read properties of undefined (reading 'baseUrl')
11 | console.log(this.configService.get('app')); // Here while running test it gets undefined
12 |
> 13 | const baseUrl = this.configService.get('app').baseUrl;
| ^
14 |
15 | return {
16 | appName: createAppDeployDto.appName,
at AppDeployService.create (app-deploy/app-deploy.service.ts:13:50)
at AppDeployController.create (app-deploy/app-deploy.controller.ts:12:34)
at Object.<anonymous> (app-deploy/app-deploy.controller.spec.ts:20:34)

In your app-deploy.controller.spec.ts, try to change this part of your code:
const module: TestingModule = await Test.createTestingModule({
controllers: [AppDeployController],
providers: [AppDeployService],
imports: [
ConfigModule.forRoot({
load: [configuration],
}),
],
}).compile();
controller = module.get<AppDeployController>(AppDeployController);

Related

NestJS Mock RabbitMQ in Jest

I have an AppModule file as follows:
import { Module } from '#nestjs/common'
import { RabbitMQModule } from '#golevelup/nestjs-rabbitmq'
#Module({
imports: [
RabbitMQModule.forRoot(RabbitMQModule, {
exchanges: [
{
name: 'my_rabbit',
type: 'direct',
},
],
uri: process.env.RABBITMQ_URI,
connectionInitOptions: { wait: true },
}),
],
})
export class AppModule {}
I have tried to mock rabbitmq using #golevelup/nestjs-rabbitmq like this:
import { Module } from '#nestjs/common'
import { RabbitMQModule } from '#golevelup/nestjs-rabbitmq'
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [
AppModule
],
})
.overrideProvider(AmqpConnection)
.useValue(createMock<AmqpConnection>())
.compile()
})
This is giving me error:
[Nest] 2745 - 24/07/2022, 17:02:54 ERROR [AmqpConnection] Disconnected from RabbitMQ broker (default)
Error: connect ECONNREFUSED 127.0.0.1:5672
If i mock the whole rabbitmq module like:
jest.mock('#golevelup/nestjs-rabbitmq')
I will get errors like:
Nest cannot create the AppModule instance.
The module at index [0] of the AppModule "imports" array is undefined.
Has anyone successfully mocked RabbitMQ? Please assist if possible.
I solve this problem mocking an AmqpConnection like this.
import { AmqpConnection } from "#nestjs-plus/rabbitmq";
import { TestingModule, Test } from "#nestjs/testing";
import { IntegrationQueueService } from "./integration-queue.service";
describe('IntegrationQueueService', () => {
type MockType<T> = {
[P in keyof T]?: jest.Mock<{}>;
};
const mockFactory: () => MockType<AmqpConnection> = jest.fn(() => ({
publish: jest.fn(() => AmqpConnection),
}))
let service: IntegrationQueueService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
IntegrationQueueService,
{
provide: AmqpConnection,
useFactory: mockFactory,
},
],
})
.compile();
service = module.get<IntegrationQueueService> (IntegrationQueueService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
})

NestJS Test No Repository was found

I have a NestJS project configured with TypeORM. The e2e test is the one that is generated by the CLI:
import { Test, TestingModule } from '#nestjs/testing';
import { INestApplication } from '#nestjs/common';
import * as request from 'supertest';
import { AppModule } from './../src/app.module';
describe('AppController (e2e)', () => {
let app: INestApplication;
beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
afterAll(async () => {
await app.close();
});
it('/ (GET)', () => {
return request(app.getHttpServer())
.get('/')
.expect(200)
.expect('Hello World!');
});
});
The error I get is the following:
RepositoryNotFoundError: No repository for "Question" was found. Looks like this entity is not registered in current "default" connection?
at RepositoryNotFoundError.TypeORMError [as constructor] (../src/error/TypeORMError.ts:7:9)
at new RepositoryNotFoundError (../src/error/RepositoryNotFoundError.ts:10:9)
at EntityManager.Object.<anonymous>.EntityManager.getRepository (../src/entity-manager/EntityManager.ts:975:19)
at Connection.Object.<anonymous>.Connection.getRepository (../src/connection/Connection.ts:354:29)
at InstanceWrapper.useFactory [as metatype] (../node_modules/#nestjs/typeorm/dist/typeorm.providers.js:17:30)
at TestingInjector.instantiateClass (../node_modules/#nestjs/core/injector/injector.js:304:55)
at callback (../node_modules/#nestjs/core/injector/injector.js:48:41)
at TestingInjector.resolveConstructorParams (../node_modules/#nestjs/core/injector/injector.js:124:24)
at TestingInjector.loadInstance (../node_modules/#nestjs/core/injector/injector.js:52:9)
AppModule is configured like this:
import { Module } from '#nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TypeOrmModule } from '#nestjs/typeorm';
import { QuestionsModule } from './questions/questions.module';
import { Connection, getConnectionOptions } from 'typeorm';
#Module({
imports: [
// docker run --name postgres_questions_answers -e POSTGRES_PASSWORD=postgres -e POSTGRES_USER=postgres -e POSTGRES_DB=dev_questions_answers -p 5432:5432 -d postgres
TypeOrmModule.forRootAsync({
useFactory: async () =>
Object.assign(await getConnectionOptions(), {
// FIXME: This is done so Jest e2e doesnt raise Cannot create a new connection named "default", because connection with such name already exist and it now has an active connection session.
keepConnectionAlive: process.env.NODE_ENV === 'test',
}),
}),
QuestionsModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {
constructor(private connection: Connection) {}
}
And the QuestionsModule like this:
import { Module } from '#nestjs/common';
import { TypeOrmModule } from '#nestjs/typeorm';
import { QuestionsService } from './questions.service';
import { QuestionsController } from './questions.controller';
import { Question } from '../entities/question.entity';
#Module({
imports: [TypeOrmModule.forFeature([Question])],
controllers: [QuestionsController],
providers: [QuestionsService],
})
export class QuestionsModule {}
Project runs fine in development and production mode, so I am wondering what am I doing wrong here?

e2e test fail on startup using NestJs and Typeorm

I use the NestJS framework and typeorm. When working with a database, all data is successfully saved. There are no problems with the connection. I try to configure e2e test with Jest. Unfortunately, I got 2 errors:
TypeError: Cannot read property 'getHttpServer' of undefined
and
No repository for "User" was found. Looks like this entity is not registered in current "default" connection?
I tried to setup test env by using this tutorial.
My files:
app.e2e-spec.ts
import { Test, TestingModule } from '#nestjs/testing';
import { INestApplication } from '#nestjs/common';
import * as request from 'supertest';
import { AppModule } from '../src/app.module';
import { TypeOrmModule } from '#nestjs/typeorm';
describe('AppController (e2e)', () => {
let app: INestApplication;
beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [
AppModule,
TypeOrmModule.forRoot({
'type': 'mssql',
'host': 'localhost',
'port': 1433,
'username': 'gift_draw_db',
'password': '',
'database': 'gift_draw_local',
'entities': ['./**/*.entity.ts'],
'synchronize': false,
}),
],
providers: [],
}).compile();
app = moduleFixture.createNestApplication();
console.error(process.env.DB_DATABASE_NAME, '<------------------------------'); // NEVER SEEN THIS
await app.init();
});
afterAll(async () => {
await app.close();
});
it('/api/ (GET)', async () => {
return request(app.getHttpServer()) // 1st error
.get('/api/')
.expect(200)
.expect('Working!');
});
});
app.module.ts
#Module({
imports: [
ConfigModule,
AuthModule,
DrawModule,
UserModule
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
user.e2e-spec.ts
describe('User', () => {
let app: INestApplication;
let repository: Repository<User>;
beforeAll(async () => {
const module = await Test.createTestingModule({
imports: [
UserModule,
TypeOrmModule.forRoot({
'type': 'mssql',
'host': 'localhost',
'port': 1433,
'username': 'gift_draw_db',
'password': '',
'database': 'gift_draw_local',
'entities': ['./**/*.entity.ts'],
'synchronize': false,
}),
],
}).compile();
app = module.createNestApplication();
repository = module.get('UserRepository');
await app.init();
});
afterEach(async () => {
await repository.query(`DELETE FROM users;`);
});
afterAll(async () => {
await app.close();
});
});
user.module.ts
#Module({
controllers: [UserController],
imports: [TypeOrmModule.forFeature([User])],
providers: [UserService],
exports: [UserService, TypeOrmModule],
})
export class UserModule {
}
config.module.ts
#Module({
imports: [
NestConfigModule.forRoot({
isGlobal: true,
load: [configuration]
}),
TypeOrmModule.forRoot(process.env.NODE_ENV === 'production' ? {...configProd} : {...configDev} )
],
})
export class ConfigModule {
}
ConfigModule has the same credentials as testing one.
The problem is that when you start the app the base dir is "dist" while when you start it with for the "e2e" tests, the base dir is "src".
You can change the entities definition to something similar to this:
entities: [__dirname + '/../**/*.entity.ts']

How to apply Global Pipes during e2e tests

How do you apply global pipes when using Test.createTestingModule?
Normally, global pipes are added when the application is mounted in main.ts.
beforeEach(async done => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule]
}).compile()
app = moduleFixture.createNestApplication()
await app.init()
done()
})
Here's what I do to ensure that the global pipes in my main.ts file are always in sync with my testing setup...
Start by creating a new file called main.config.ts, this should contain your global pipes, filters, etc:
import { INestApplication, ValidationPipe } from "#nestjs/common";
export function mainConfig(app: INestApplication) {
app.enableCors();
app.useGlobalPipes(new ValidationPipe());
}
Next, use the newly created mainConfig function in both the main.ts and app.e2e-spec.ts (or wherever you setup your tests):
main.ts
import { NestFactory } from "#nestjs/core";
import { mainConfig } from "main.config";
import { AppModule } from "./app.module";
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// use here
mainConfig(app);
await app.listen(3000);
}
bootstrap();
app.e2e-spec.ts
import { INestApplication } from "#nestjs/common";
import { TestingModule, Test } from "#nestjs/testing";
import { AppModule } from "app.module";
import { mainConfig } from "main.config";
let app: INestApplication;
beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
// use here
mainConfig(app);
await app.init();
});
Now if you need to add a new pipe, you'll only have to make the change in one spot (main.config.ts) to see it reflected in both the app and the tests.
You can add them before you initialize the testing module:
beforeEach(async done => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule]
}).compile()
app = moduleFixture.createNestApplication()
// Add global pipe here
app.useGlobalPipes(new ValidationPipe({ transform: true, whitelist: true, forbidNonWhitelisted: true }))
await app.init()
done()
})
Another approach is to declare the global pipes in the module using APP_PIPE.
#Module({
providers: [
{
provide: APP_PIPE,
useClass: ValidationPipe,
},
],
})
export class AppModule {}
Then it will be available in your e2e tests.

How to implement integration testing for guards in nest js?

I want to implement unit testing for guards using nest js(Jest JS). I could nt find much documentation.
import {Test, TestingModule} from '#nestjs/testing';
import {CatsController} from '../src/modules/cats/cats.controller';
import {CatsService} from '../src/modules/cats/cats.service';
import {ICat} from '../src/modules/cats/interfaces/ICat';
import {JwtStrategy} from '../src/strategy/AppId.strategy';
beforeEach(async () => {
const module = await Test.createTestingModule({
controllers: [CatsController],
providers: [CatsService],
}).compile();
catsService = module.get<CatsService>(CatsService);
catsController = module.get<CatsController>(CatsController);
});
describe('findAll', () => {
it('should return an array of cats', async () => {
// const appIdAuthContext: AppIDAuthToken = tokenInfo;
const result: ICat = {
name: 'test',
age: 1,
breed: 'one'
};
jest.spyOn(catsService, 'findAll').mockImplementation(() => result);
console.log(result);
console.log(catsController.findAll());
expect(await catsController.findAll()).toBe(result);
});
});
The above code will return array of cats. i want to implement guards for this unit test.
I have found an implementation.i'm sharing the sample , it may help somebody
function createTestModule() {
return Test.createTestingModule({
imports: [
PassportModule.register({defaultStrategy: 'jwt'}), CatsModule
],
controllers: [
AppController
],
providers: [
AppService, RoleGuard, JwtStrategy
],
exports: [PassportModule]
}).compile();
}
beforeEach(async () => {
const module = (await createTestModule(
));
app = module.createNestApplication();
server = app.getHttpServer();
await app.init();
});
it('Should create cat for admin', async () => {
return await request(server)
.post('/cats/createcat')
.set('Authorization', catAdminToken)
.set('Accept', 'application/json')
.send(catDto)
.expect(201);
});

Resources