How to read config value using ConfigModule inside of a provider? - javascript

I would like to add a config module to my nestjs application and use some config in my database module.
For example I would like to get the host value of my configuration:
In a class I can do const dbHost = this.configService.get<string>('database.host');, but in my code I need the value inside of a provider. How do I get config values inside of a provider?
app.module.ts
#Module({
imports: [
DatabaseModule,
ConfigModule.forRoot({
load: [configuration],
}),
],
})
export class AppModule {}
database.module.ts
import { Module, Inject } from '#nestjs/common'
import { MongoClient, MongoClientOptions, Db, Logger } from 'mongodb'
#Module({
providers: [
{
provide: 'DATABASE_CLIENT',
useFactory: () => ({ client: null })
},
{
provide: 'DATABASE_CONNECTION',
inject: ['DATABASE_CLIENT'],
useFactory: async (dbClient): Promise<Db> => {
Logger.setLevel('debug')
// How do I get the host value of my config?
const mongo: string = 'mongodb://127.0.0.1:27017'
const database: string = 'data'
const options: MongoClientOptions = {}
const client = new MongoClient(mongo, options)
await client.connect()
const db = client.db(database)
return db
}
}
],
exports: ['DATABASE_CONNECTION', 'DATABASE_CLIENT'],
import: [ConfigModule]
})
export class DatabaseModule {
constructor(#Inject('DATABASE_CLIENT') private dbClient) {}
onApplicationShutdown(signal: string) {
if (signal) console.log(signal + ' signal recieved')
}
}

In your inject add ConfigService, then it'll be the second value of useFactory's parameters.
#Module({
providers: [
{
provide: 'DATABASE_CLIENT',
useFactory: () => ({ client: null })
},
{
provide: 'DATABASE_CONNECTION',
inject: ['DATABASE_CLIENT', ConfigService],
useFactory: async (dbClient, config: ConfigService): Promise<Db> => {
Logger.setLevel('debug')
const dbHost = config.get('database.host')
const mongo: string = 'mongodb://127.0.0.1:27017'
const database: string = 'data'
const options: MongoClientOptions = {}
const client = new MongoClient(mongo, options)
await client.connect()
const db = client.db(database)
return db
}
}
],
exports: ['DATABASE_CONNECTION', 'DATABASE_CLIENT'],
import: [ConfigModule]
})
export class DatabaseModule {
constructor(#Inject('DATABASE_CLIENT') private dbClient) {}
onApplicationShutdown(signal: string) {
if (signal) console.log(signal + ' signal recieved')
}
}

Related

Error: Circular dependency - NestJS Testing

When I create a test of my controller I get the following error:
A circular dependency has been detected. Please make sure that each
side of a bidirectional relationship is decorated with "forwardRef()"
// category.service.ts
constructor(
#InjectModel(Category.name) private categoryModel: Model<CategoryDocument>,
private readonly subcategoriesService: SubcategoriesService,
) {}
// category.controller.spec.ts
describe('V1CategoriesController', () => {
let categoriesController: CategoriesController;
let categoriesService: CategoriesService;
beforeEach(async () => {
const moduleRef: TestingModule = await Test.createTestingModule({
controllers: [CategoriesController],
providers: [CategoriesService, SubcategoriesService],
}).compile();
categoriesController = moduleRef.get<CategoriesController>(
CategoriesController,
);
categoriesService = moduleRef.get<CategoriesService>(CategoriesService);
jest.clearAllMocks();
});
it('CategoriesController - should be defined', () => {
expect(categoriesController).toBeDefined();
});
});
I tried to create a SubcategoriesService Mock, but I'm not sure it's the right way to do it
const SubcategoriesService = jest.fn().mockReturnValue({
findByCategory: jest.fn().mockResolvedValue([]),
});
const moduleRef: TestingModule = await Test.createTestingModule({
controllers: [CategoriesController],
providers: [CategoriesService, { provide: 'SubcategoriesService', useFactory: SubcategoriesService },],
}).compile();
For your main service you are using a Mock?
There is a possibility that you are not using a mock or it has the wrong name.

Angular: testing login function

I'm moving my first steps in Angular testing. In particular, I am trying to test this function in LoginComponent
login() {
this.restApi.login(this.loginForm.value).subscribe((resp) => {
this.user.username = (resp as any).user;
this.user.token = (resp as any).tkn;
this.router.navigate(['/main']);
});
}
restApi is an instance of RestApiService, a service that makes http calls. To test this function, I wrote this test:
describe('LoginComponent', () => {
let component: LoginComponent;
let fixture: ComponentFixture<LoginComponent>;
let user = new UserService();
let httpMock: HttpTestingController;
let httpClient: HttpClient;
let testRouter = {
navigate: jasmine.createSpy('navigate'),
};
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [ReactiveFormsModule, FormsModule, HttpClientTestingModule],
declarations: [LoginComponent],
providers: [
{ provide: Router, useValue: testRouter },
{ provide: UserService, useValue: user },
RestApiService,
],
}).compileComponents();
httpClient = TestBed.inject(HttpClient);
httpMock = TestBed.inject(HttpTestingController);
}));
function updateForm() {
component.loginForm.controls['username'].setValue('bob');
component.loginForm.controls['password'].setValue('bobspassword');
}
beforeEach(() => {
fixture = TestBed.createComponent(LoginComponent);
component = fixture.componentInstance;
component.ngOnInit();
fixture.detectChanges();
});
it('should login', () => {
const loginRes = {
res: 1,
spec: 'ok',
tkn: 'aToken',
user: 'bob',
};
const loginReq = {
username: 'bob',
password: 'bobspassword',
};
updateForm();
component.login();
expect(component.user.token).toBeDefined();
expect(testRouter.navigate).toHaveBeenCalledWith(['/main']);
const req = httpMock.expectOne(
'http://myServerAddress:8080/restEndpoint/login'
);
expect(req.request.method).toBe('POST');
expect(req.request.body).toEqual(loginReq);
req.flush(loginRes);
});
});
The test fails because component.user.token is undefined and it doesn't navigate to /main
Can you help me?
Thanks
This should be handled using Mock rather than making actual http call by calling the service.
export class MockRestApiService{
login(){
return of({
user: 'someName',
tkn: 'someToken'
})
}
}
Now import this Mock class in spec as below:
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [ReactiveFormsModule, FormsModule, HttpClientTestingModule],
declarations: [LoginComponent],
providers: [
{ provide: Router, useValue: testRouter },
{ provide: UserService, useValue: user },
{provide: RestApiService, useClass: MockRestApiService},
],
}).compileComponents();
httpClient = TestBed.inject(HttpClient);
httpMock = TestBed.inject(HttpTestingController);
}));
This will return someToken as the value for the called function login()
Try to take a look at this article where I have explained a component code with proper mocking.. since you are new to testing, I would recommend this article to start with

Nestjs mocking service constructor with Jest

I have created following service to use twilio send login code sms to users:
sms.service.ts
import { Injectable, Logger } from '#nestjs/common';
import * as twilio from 'twilio';
Injectable()
export class SmsService {
private twilio: twilio.Twilio;
constructor() {
this.twilio = this.getTwilio();
}
async sendLoginCode(phoneNumber: string, code: string): Promise<any> {
const smsClient = this.twilio;
const params = {
body: 'Login code: ' + code,
from: process.env.TWILIO_SENDER_NUMBER,
to: phoneNumber
};
smsClient.messages.create(params).then(message => {
return message;
});
}
getTwilio() {
return twilio(process.env.TWILIO_SID, process.env.TWILIO_SECRET);
}
}
sms.service.spec.js that contains my test
import { Test, TestingModule } from '#nestjs/testing';
import { SmsService } from './sms.service';
import { Logger } from '#nestjs/common';
describe('SmsService', () => {
let service: SmsService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [SmsService]
}).compile();
service = module.get<SmsService>(SmsService);
});
describe('sendLoginCode', () => {
it('sends login code', async () => {
const mockMessage = {
test: "test"
}
jest.mock('twilio')
const twilio = require('twilio');
twilio.messages = {
create: jest.fn().mockImplementation(() => Promise.resolve(mockMessage))
}
expect(await service.sendLoginCode("4389253", "123456")).toBe(mockMessage);
});
});
});
How can I use jest create mock of the SmsService constructor so that it's twilio variable gets set to mocked version of it I create in service.spec.js?
You should inject your dependency instead of using it directly, then you can mock it in your test:
Create a custom provider
#Module({
providers: [
{
provide: 'Twillio',
useFactory: async (configService: ConfigService) =>
twilio(configService.TWILIO_SID, configService.TWILIO_SECRET),
inject: [ConfigService],
},
]
Inject it in your service
constructor(#Inject('Twillio') twillio: twilio.Twilio) {}
Mock it in your test
const module: TestingModule = await Test.createTestingModule({
providers: [
SmsService,
{ provide: 'Twillio', useFactory: twillioMockFactory },
],
}).compile();
See this thread on how to create mocks.

How to mock third party library using jest

I am developing a node.js application using nestjs
I have a class called LoggerService as below
export class LoggerService {
private logger: Rollbar;
constructor() {
this.logger = this.setupLogger();
}
critical(...args: Array<string | Error | object | Date | any[]>) {
this.logger.error(...args);
}
private setupLogger(): Rollbar {
if (this.logger == null) {
this.logger = new Rollbar({
accessToken: 'token',
environment: 'dev',
captureUncaught: true,
captureUnhandledRejections: true,
});
}
return this.logger;
}
Now I am writing unit test for this class using jest as below.
describe('LoggerService.log', () => {
let service: LoggerService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [LoggerService],
}).compile();
service = module.get<LoggerService>(LoggerService);
});
it('critical', () => {
service.critical('error','message');
expect(???).toHaveBeenCalledWith('error', 'message')
})
);
My question is how to check(expect) if logger.error is called, or how to mock Rollbar in this class.
1) Provide your external dependency/library as an injectable token in your module
#Module({
providers: [
{
provide: 'Rollbar',
useFactory: async (configService: ConfigService) => new Rollbar({
accessToken: configService.accessToken,
environment: configService.environment,
captureUncaught: true,
captureUnhandledRejections: true,
}),
inject: [ConfigService],
},
]
2) Inject it in your LoggerService instead of creating it
export class LoggerService {
constructor(#Inject('Rollbar') private logger: Rollbar) {
}
3) Now you can mock your dependency in your test
const module: TestingModule = await Test.createTestingModule({
providers: [
LoggerService,
{ provide: 'Rollbar', useFactory: rollbarMockFactory },
],
}).compile();

Inject TypeORM repository into NestJS service for mock data testing

There's a longish discussion about how to do this in this issue.
I've experimented with a number of the proposed solutions but I'm not having much luck.
Could anyone provide a concrete example of how to test a service with an injected repository and mock data?
Let's assume we have a very simple service that finds a user entity by id:
export class UserService {
constructor(#InjectRepository(UserEntity) private userRepository: Repository<UserEntity>) {
}
async findUser(userId: string): Promise<UserEntity> {
return this.userRepository.findOne(userId);
}
}
Then you can mock the UserRepository with the following mock factory (add more methods as needed):
// #ts-ignore
export const repositoryMockFactory: () => MockType<Repository<any>> = jest.fn(() => ({
findOne: jest.fn(entity => entity),
// ...
}));
Using a factory ensures that a new mock is used for every test.
describe('UserService', () => {
let service: UserService;
let repositoryMock: MockType<Repository<UserEntity>>;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
UserService,
// Provide your mock instead of the actual repository
{ provide: getRepositoryToken(UserEntity), useFactory: repositoryMockFactory },
],
}).compile();
service = module.get<UserService>(UserService);
repositoryMock = module.get(getRepositoryToken(UserEntity));
});
it('should find a user', async () => {
const user = {name: 'Alni', id: '123'};
// Now you can control the return value of your mock's methods
repositoryMock.findOne.mockReturnValue(user);
expect(service.findUser(user.id)).toEqual(user);
// And make assertions on how often and with what params your mock's methods are called
expect(repositoryMock.findOne).toHaveBeenCalledWith(user.id);
});
});
For type safety and comfort you can use the following typing for your (partial) mocks (far from perfect, there might be a better solution when jest itself starts using typescript in the upcoming major releases):
export type MockType<T> = {
[P in keyof T]?: jest.Mock<{}>;
};
My solution uses sqlite memory database where I insert all the needed data and create schema before every test run. So each test counts with the same set of data and you do not have to mock any TypeORM methods:
import { Test, TestingModule } from "#nestjs/testing";
import { CompanyInfo } from '../../src/company-info/company-info.entity';
import { CompanyInfoService } from "../../src/company-info/company-info.service";
import { Repository, createConnection, getConnection, getRepository } from "typeorm";
import { getRepositoryToken } from "#nestjs/typeorm";
describe('CompanyInfoService', () => {
let service: CompanyInfoService;
let repository: Repository<CompanyInfo>;
let testingModule: TestingModule;
const testConnectionName = 'testConnection';
beforeEach(async () => {
testingModule = await Test.createTestingModule({
providers: [
CompanyInfoService,
{
provide: getRepositoryToken(CompanyInfo),
useClass: Repository,
},
],
}).compile();
let connection = await createConnection({
type: "sqlite",
database: ":memory:",
dropSchema: true,
entities: [CompanyInfo],
synchronize: true,
logging: false,
name: testConnectionName
});
repository = getRepository(CompanyInfo, testConnectionName);
service = new CompanyInfoService(repository);
return connection;
});
afterEach(async () => {
await getConnection(testConnectionName).close()
});
it('should be defined', () => {
expect(service).toBeDefined();
});
it('should return company info for findOne', async () => {
// prepare data, insert them to be tested
const companyInfoData: CompanyInfo = {
id: 1,
};
await repository.insert(companyInfoData);
// test data retrieval itself
expect(await service.findOne()).toEqual(companyInfoData);
});
});
I got inspired here: https://gist.github.com/Ciantic/be6a8b8ca27ee15e2223f642b5e01549
Similar to best practices in other frameworks you can use a test DB instead of a mock.
describe('EmployeesService', () => {
let employeesService: EmployeesService;
let moduleRef: TestingModule;
beforeEach(async () => {
moduleRef = await Test.createTestingModule({
imports: [
TypeOrmModule.forRoot({
type: 'postgres',
url: 'postgres://postgres:#db:5432/test', // read this from env
autoLoadEntities: true,
synchronize: true,
dropSchema: true,
}),
],
providers: [EmployeesService],
}).compile();
employeesService = moduleRef.get<EmployeesService>(EmployeesService);
});
afterEach(async () => {
await moduleRef.close();
});
describe('findOne', () => {
it('returns empty array', async () => {
expect(await employeesService.findAll()).toStrictEqual([]);
});
});
});
Real-life example in resolver specs in: https://github.com/thisismydesign/nestjs-starter
Last tested with typeorm#0.3.7 and #nestjs/typeorm#9.0.0.
I also found that this worked for me:
export const mockRepository = jest.fn(() => ({
metadata: {
columns: [],
relations: [],
},
}));
and
const module: TestingModule = await Test.createTestingModule({
providers: [{ provide: getRepositoryToken(Entity), useClass: mockRepository }],
}).compile();
Starting with the above ideas and to help with mocking any class, we came out with this MockFactory:
export type MockType<T> = {
[P in keyof T]?: jest.Mock<unknown>;
};
export class MockFactory {
static getMock<T>(type: new (...args: any[]) => T, includes?: string[]): MockType<T> {
const mock: MockType<T> = {};
Object.getOwnPropertyNames(type.prototype)
.filter((key: string) => key !== 'constructor' && (!includes || includes.includes(key)))
.map((key: string) => {
mock[key] = jest.fn();
});
return mock;
}
}
const module: TestingModule = await Test.createTestingModule({
providers: [
{
provide: getRepositoryToken(MyCustomRepository),
useValue: MockFactory.getMock(MyCustomRepository)
}
]
}).compile();
First of all I'm new to Ts/Js/Node.
Here is my example code : it lets you use NEST's injection system with a custom Connection during tests.
In this manner service/controller objects are not created by hand but wired by the TestingModule:
import { Test } from '#nestjs/testing';
import { getRepositoryToken } from '#nestjs/typeorm';
import {
Repository,
createConnection,
getConnection,
getRepository,
Connection,
} from 'typeorm';
import { Order } from './order';
import { OrdersService } from './orders.service';
describe('Test Orders', () => {
let repository: Repository<Order>;
let service: OrdersService;
let connection: Connection;
beforeEach(async () => {
connection = await createConnection({
type: 'sqlite',
database: './test.db',
dropSchema: true,
entities: [Order],
synchronize: true,
logging: true,
});
repository = getRepository(Order);
const testingModule = await Test.createTestingModule({
providers: [
OrdersService,
{
provide: getRepositoryToken(Order, connection),
useFactory: () => {
return repository;
},
},
],
}).compile();
console.log('Getting Service from NEST');
service = testingModule.get<OrdersService>(OrdersService);
return connection;
});
afterEach(async () => {
await getConnection().close();
});
it('should be defined', () => {
expect(service).toBeDefined();
});
it('CRUD Order Test', async () => {
const order = new Order();
order.currency = 'EURO';
order.unitPrice = 12.0;
order.issueDate = new Date();
const inserted = await service.create(order);
console.log('Inserted order ', inserted.id); // id is the #PrimaryGeneratedColumn() key
let allOrders = await service.findAll();
expect(allOrders.length).toBe(1);
await service.delete(inserted.id);
allOrders = await service.findAll();
expect(allOrders.length).toBe(0);
});
});
Something similar to the suggested MockTypes defined in the previous answer is the TypedMockType
type ArgsType<T> = T extends (...args: infer A) => unknown ? A : never;
export type TypedMockType<T> = {
// eslint-disable-next-line #typescript-eslint/no-explicit-any
[P in keyof T]: T[P] extends (...args: any) => unknown
? jest.Mock<ReturnType<T[P]>, ArgsType<T[P]>>
: never;
};
This is a utility type that can be used the same as MockType, but the difference is that your payloads of the original method signature will be the same.

Categories