import { Injectable, Logger, OnModuleInit } from '#nestjs/common';
import { Client, ClientGrpc } from '#nestjs/microservices';
import { catchError, map } from 'rxjs';
import { ICmdUserUsecase } from 'src/adaptor/usecase/ICmdUserUsecase';
import { dataServiceGrpcOptions } from 'src/app/data.options';
import { ICmdRepo } from 'src/adaptor/repo/ICmdRepo';
#Injectable()
export class CmdUserUsecase implements ICmdUserUsecase, OnModuleInit {
private dataCmdRepo: ICmdRepo;
constructor(private readonly logger: Logger) {}
#Client({
...dataServiceGrpcOptions,
})
dataServiceClientGrpc: ClientGrpc;
onModuleInit() {
this.dataCmdRepo = this.dataServiceClientGrpc.getService<ICmdRepo>('DataService');
}
async createUserProfile(data: any) {
return new Promise((resolve, reject) => {
const userData = this.dataCmdRepo.createUser(data).pipe(
catchError((err) => {
if (err.code === 2) {
err.message = 'Data Server is down';
throw new Error(err.message);
} else {
throw new Error(err.message);
}
}),
map((results) => {
return results;
}),
);
userData.subscribe(
(rate) => {
resolve(rate);
},
(error) => {
reject(error);
},
);
});
}
async createUser(data: any): Promise<any> {
return await this.createUserProfile(data)
.then((res) => {
this.logger.log(res);
return res;
})
.catch((err) => {
return err;
});
}
}
here is the client service code which is utilizing the grpc services, every time I am getting connection not found from my spec file,
how can I mock this foresaid grpc service in spec files.
import { INestApplication, Logger } from '#nestjs/common';
import { ConfigModule } from '#nestjs/config';
import { ClientsModule } from '#nestjs/microservices';
import { Test, TestingModule } from '#nestjs/testing';
import { ICmdUserUsecase } from 'src/adaptor/usecase/ICmdUserUsecase';
import { IQueryUserUsecase } from 'src/adaptor/usecase/IQueryUserUsecase';
import { userServiceGrpcOptions } from 'src/app/data.options';
import { configuration } from 'src/utils/configuration';
import { Mock } from 'src/utils/Mocks/mock';
import { MetricsTelemetryModule } from 'telemetry-lib';
import { QueryUserUsecase } from '../query-user-usecase/query.user.usecase';
import { CmdUserUsecase } from './cmd.user.usecase';
describe('CMD user usecase ', () => {
let app: INestApplication;
let cmdUserUsecase: CmdUserUsecase;
beforeEach(async () => {
const moduleRef: TestingModule = await Test.createTestingModule({
imports: [
ConfigModule.forRoot({
load: [configuration],
isGlobal: true,
cache: true,
}),
MetricsTelemetryModule.forRoot({
apiMetrics: true,
}),
ClientsModule.register([
{
name: 'USER_SERVICE',
...userServiceGrpcOptions,
},
]),
],
providers: [
CmdUserUsecase,
{
provide: ICmdUserUsecase,
useClass: CmdUserUsecase,
},
{
provide: IQueryUserUsecase,
useClass: QueryUserUsecase,
},
{
provide: Logger,
useValue: {
log: jest.fn((data) => data),
},
},
],
exports: [],
}).compile();
app = moduleRef.createNestApplication();
await app.init();
cmdUserUsecase = moduleRef.get<CmdUserUsecase>(CmdUserUsecase);
});
afterAll(async () => {
await app.close();
});
describe('root', () => {
it('CmdUserUsecase should be defined', () => {
expect(cmdUserUsecase).toBeDefined();
});
it('CmdUserUsecase"s createUser should be called when invoked', async () => {
try {
await cmdUserUsecase.createUser(Mock.userMock);
expect(cmdUserUsecase.createUser).toHaveBeenCalled();
} catch (error) {
expect(error).toEqual(error);
}
});
it('CmdUserUsecase"s createUser should RETURN suceess message', async () => {
try {
const result = await cmdUserUsecase.createUser(Mock.userMock);
expect(result.status).toBe('success');
} catch (error) {
expect(error).toEqual(error);
}
});
it('CmdUserUsecase"s createUserProfile should be called when invoked', async () => {
try {
await cmdUserUsecase.createUserProfile(Mock.userMock);
expect(cmdUserUsecase.createUserProfile).toHaveBeenCalled();
} catch (error) {
expect(error).toEqual(error);
}
});
it('CmdUserUsecase"s createUserProfile should RETURN suceess message', async () => {
try {
const result = await cmdUserUsecase.createUserProfile(Mock.userMock);
expect(result).toBe('success');
} catch (error) {
expect(error).toEqual(error);
}
});
});
});
How can I modify the above spec file for so that it can contain the mock of the grpc clients as mentioned in the above code.
So after reading a lot of articles I found 2 ways to mock GRPC and use it in the client calls.
Use GRPC-MOCK npm package,
Create a Grpc mock server for the given spec file and add the services at run time. Like
import * as grpc from '#grpc/grpc-js';
import * as protoLoader from '#grpc/proto-loader';
import { INestApplication, Logger } from '#nestjs/common';
import { ConfigModule } from '#nestjs/config';
import { Test, TestingModule } from '#nestjs/testing';
import { IHandlebarJs } from 'src/adaptor/handlebarjs/IHandlebarJs';
import { IMatrixQueryRepo } from 'src/adaptor/repo/IMatrixQueryRepo';
import { Handlebars } from 'src/helper/HandlebarsJs';
import { MatrixQueryRepo } from 'src/infrastructure/repository/MatrixQueryRepo';
import { configuration } from 'src/utils/configuration';
import { Mock } from 'src/utils/mock';
import { QueryMatrixUsecase } from './matrix.service';
const packageDef = protoLoader.loadSync('src/app/proto/as.proto', {});
const grpcObject = grpc.loadPackageDefinition(packageDef);
const asdata: any = grpcObject.asdata;
const server = new grpc.Server();
server.bindAsync(process.env.AS_DATA_SERVICE_URL, grpc.ServerCredentials.createInsecure(), (e, r) => {
console.log(e, r);
server.start();
});
server.addService(asdata.DataService.service, {
SaveAddressGeocode: (call, callback) => {
console.log(call.request);
callback(null, Mock.dsMockResponse);
},
GetAddressGeocode: (call, callback) => {
console.log(call.request);
callback(null, Mock.dsMockResponse);
},
SaveMatrixDistanceAndETA: (call, callback) => {
console.log(call.request);
callback(null, Mock.dsMockResponse);
},
GetMatrixDistanceAndETA: (call, callback) => {
console.log(call.request);
callback(null, Mock.dsMockResponse);
},
});
describe('QueryMatrixUsecase', () => {
let service: QueryMatrixUsecase;
let app: INestApplication;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [
ConfigModule.forRoot({
load: [configuration],
isGlobal: true,
cache: true,
}),
],
providers: [
QueryMatrixUsecase,
Logger,
{
provide: IMatrixQueryRepo,
useClass: MatrixQueryRepo,
},
{
provide: IHandlebarJs,
useClass: Handlebars,
},
],
}).compile();
app = module.createNestApplication();
await app.init();
service = module.get<QueryMatrixUsecase>(QueryMatrixUsecase);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
it('QueryMatrixUsecase GetRouteMatrix should return a object with status as success', async () => {
const res = await service.GetRouteMatrix(Mock.routeDTOMock);
expect(res.success).toBe('true');
});
});
Related
In a NestJS project using Jest test the service case. I created a find method and use it in a resolver(GraphQL) function. When test the resolver, it can't find the find method in the service.
src/serivce/post.ts
import { Injectable } from '#nestjs/common';
import { InjectRepository } from '#nestjs/typeorm';
import { AbstractPostRepository } from '#abstractRepository/AbstractPostRepository';
import { PostRepository } from '#repository/PostRepository';
#Injectable()
export class PostService {
constructor(
#InjectRepository(PostRepository)
private postRepo: AbstractPostRepository,
) {}
async findById(id: string) {
const posts = await this.postRepo.findById(id);
......
return posts;
}
}
src/resolver/query/post.ts
import { Args, Query, Resolver } from '#nestjs/graphql';
import { Authorized } from '#lib/auth/authorized';
import { PermissionScope } from '#lib/auth/permissionScope';
import { PostService } from '#service/postService';
#Resolver()
export class PostsResolver {
constructor(private postService: PostService) {}
#PermissionScope()
#Authorized([
PermissionEnum.READ_MANAGEMENT,
])
#Query()
async findPosts() {
return await this.postService.findById(id);
}
}
The unit test for a resolver related this service:
test/unit/resolver/post.spec.ts
import { Test, TestingModule } from '#nestjs/testing';
import { PERMISSION_MANAGER } from '#lib/auth/permissionManager.module';
import { PostsResolver } from '#resolver/query/post';
import { PostService } from '#service/postService';
describe('PostsResolver', () => {
let resolver: PostsResolver;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
PostsResolver,
{
provide: PERMISSION_MANAGER,
useValue: jest.fn(),
},
{
provide: PostService,
useFactory: () => ({
findById: jest.fn(() => [
{
id: '11111111-1111-1111-1111-111111111111',
name: 'name1',
},
]),
}),
},
],
}).compile();
resolver = module.get<PostsResolver>(PostsResolver);
});
describe('findPosts', () => {
it('should return data', async () => {
const posts = await resolver.findPosts({
id: '11111111-1111-1111-1111-111111111111',
});
const expected = [
{
id: '11111111-1111-1111-1111-111111111111',
name: 'name1',
},
];
expect(posts).toEqual(expected);
});
});
});
When run this test got error:
● PostsResolver › findPosts › should return data
TypeError: Cannot read properties of undefined (reading 'findById')
22 | { id }: FindPostsInput,
23 | ) {
> 24 | return await this.postService.findById(
| ^
25 | id,
26 | );
at PostsResolver.findPosts (src/resolver/query/post.ts:24:41)
at Object.<anonymous> (test/unit/resolver/post.spec.ts:59:42)
It seems the mock service findById in the Test.createTestingModule doesn't work. How to mock correctly? Is it related some inject reasons?
When I tried with basic repository provided typeorm, I think test is completed.
But to do 'unit test' with custom repository of typeorm is not working.
I think mocking custom repository has problem.
What I have to do for mocking custom repostitory?
Next are test file and source file to test.
Thanks.
quests.service.spec.ts
import { Test } from '#nestjs/testing';
import { getRepositoryToken } from '#nestjs/typeorm';
import { QuestsService } from 'src/quests/quests.service';
import { QuestRepository } from 'src/quests/repositories/quest.repository';
import { Repository } from 'typeorm';
import { Complete } from 'src/quests/entities/complete.entity';
import { Player } from 'src/players/entities/player.entity';
const mockRepository = () => ({
create: jest.fn(),
save: jest.fn(),
findOne: jest.fn(),
});
const mockQuestsRepository = {
save: jest.fn(),
findOne: jest.fn(),
findAllWithCompletes: jest.fn(),
findOneWithCompletes: jest.fn(),
};
type MockRepository<T = any> = Partial<Record<keyof Repository<T>, jest.Mock>>;
type MockQuestRepository = Partial<Record<keyof QuestRepository, jest.Mock>>;
describe('QuestsService', () => {
let service: QuestsService;
let playersRepository: MockRepository<Player>;
let completeRepository: MockRepository<Complete>;
let questsRepository: MockQuestRepository;
beforeAll(async () => {
const module = await Test.createTestingModule({
providers: [
QuestsService,
{
provide: getRepositoryToken(Player),
useValue: mockRepository(),
},
{
provide: getRepositoryToken(Complete),
useValue: mockRepository(),
},
{
provide: QuestRepository,
useValue: mockQuestsRepository,
},
],
}).compile();
service = module.get<QuestsService>(QuestsService);
playersRepository = module.get(getRepositoryToken(Player));
completeRepository = module.get(getRepositoryToken(Complete));
questsRepository = module.get(QuestRepository);
});
describe('questComplete', () => {
it('should fail if quest does not exist', async () => {
questsRepository.findOne.mockResolvedValue(undefined);
const result = await service.questComplete(-1, 1);
expect(result).toEqual({
ok: false,
message: 'cant find requested Quest.',
});
});
it('should fail if player does not exist', async () => {
questsRepository.findOne.mockResolvedValue(true);
playersRepository.findOne.mockResolvedValue(undefined);
const result = await service.questComplete(1, 1);
expect(result).toEqual({
ok: false,
message: 'cant find player.',
});
});
});
});
quests.service.ts
#Injectable()
export class QuestsService {
constructor(
#InjectRepository(Complete)
private readonly completes: Repository<Complete>,
private readonly quests: QuestRepository
) {}
async questComplete(questId: number, playerId: number) {
try {
const quest = await this.quests.findOne({ id: questId });
if (!quest)
return { ok: false, message: 'cant find requested Quest.' };
const player = await Player.findOne({ where: { id: playerId } });
if (!player)
return { ok: false, message: 'cant find player.' };
const isCompleted = await this.completes.findOne({ quest, player });
if (isCompleted)
return { ok: false, message: 'quest is already completed.' };
await this.completes.save(this.completes.create({ quest, player }));
return { ok: true };
} catch (error) {
return { ok: false, message: 'quest cant be completed.' };
}
}
}
I need help adding unit test to the function below in NestJs.
I have a class with a createOrder function as shown below. the constructor of the class injects an Entity Manager. How can I test for the createOrder function in jest.
import { Injectable } from '#nestjs/common';
import * as shortId from 'shortid';
import { EntityManager, Repository } from 'typeorm';
import { HttpException, HttpStatus } from '#nestjs/common';
import { Service } from 'models/service.model';
#Injectable()
export class OrderService {
private readonly orderRepository: Repository<Service>;
constructor(private readonly entityManager: EntityManager) {
this.orderRepository = entityManager.getRepository(Service);
}
async createOrder(data) {
const orderService = new Service();
orderService.id = shortId.generate(); // just to generate a string for id
const orderServiceData = Object.assign(orderService, data);
try {
await this.orderRepository.save(orderServiceData);
return { success: true };
} catch (err) {
throw new HttpException('Post not found', HttpStatus.NOT_FOUND);
}
}
}
This is what I have tried so far. Yet it fails to call the save function
import { Test, TestingModule } from '#nestjs/testing';
import { OrderService } from './order_service.service';
import { Service } from '../../models/service.model';
import { Repository, EntityManager, getRepository } from 'typeorm';
import { getRepositoryToken } from '#nestjs/typeorm';
describe('Order Service', () => {
let orderService: OrderServiceService;
let orderRepository: Repository<Service>;
const mockOrderRepository = () => ({
save: jest.fn(),
});
const mockEntityManager = () => ({
getRepository: jest.fn(),
});
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
OrderService,
{
provide: EntityManager,
useFactory: mockEntityManager,
},
{
provide: getRepositoryToken(Service),
useFactory: mockOrderRepository,
},
],
}).compile();
orderService = await module.get<OrderService>(
OrderService,
);
orderRepository = await module.get(getRepositoryToken(Service));
});
it('should check that order service is defined', () => {
expect(orderService).toBeDefined();
});
describe('Create order service', () => {
it('should create an order service', () => {
expect(orderRepository.save).not.toHaveBeenCalled();
const data = {
name: 'Gucci Cloths',
type: 'Cloths',
};
orderService.createOrder(data);
expect(orderRepository.save).toHaveBeenCalled();
});
});
});
What you can do is mocking the save function of the orderRepository:
const mockRepository = {
save: jest.fn(),
}
const mockEntityManager = () => ({
getRepository: () => mockRepository,
});
This way you can test the function and also check that the save function has been called with the right parameters.
I have started to work with NestJS and have a question about mocking guards
for unit-test.
I'm trying to test a basic HTTP controller that has a method Guard attach to it.
My issue started when I injected a service to the Guard (I needed the ConfigService for the Guard).
When running the test the DI is unable to resolve the Guard
● AppController › root › should return "Hello World!"
Nest can't resolve dependencies of the ForceFailGuard (?). Please make sure that the argument at index [0] is available in the _RootTestModule context.
My force fail Guard:
import { Injectable, CanActivate, ExecutionContext } from '#nestjs/common';
import { ConfigService } from './config.service';
#Injectable()
export class ForceFailGuard implements CanActivate {
constructor(
private configService: ConfigService,
) {}
canActivate(context: ExecutionContext) {
return !this.configService.get().shouldFail;
}
}
Spec file:
import { CanActivate } from '#nestjs/common';
import { Test, TestingModule } from '#nestjs/testing';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ForceFailGuard } from './force-fail.guard';
describe('AppController', () => {
let appController: AppController;
beforeEach(async () => {
const mock_ForceFailGuard = { CanActivate: jest.fn(() => true) };
const app: TestingModule = await Test
.createTestingModule({
controllers: [AppController],
providers: [
AppService,
ForceFailGuard,
],
})
.overrideProvider(ForceFailGuard).useValue(mock_ForceFailGuard)
.overrideGuard(ForceFailGuard).useValue(mock_ForceFailGuard)
.compile();
appController = app.get<AppController>(AppController);
});
describe('root', () => {
it('should return "Hello World!"', () => {
expect(appController.getHello()).toBe('Hello World!');
});
});
});
I wasn't able to find examples or documentation on this issues. Am i missing something or is this a real issue ?
Appreciate any help,
Thanks.
There are 3 issues with the example repo provided:
There is a bug in Nestjs v6.1.1 with .overrideGuard() - see https://github.com/nestjs/nest/issues/2070
I have confirmed that its fixed in 6.5.0.
ForceFailGuard is in providers, but its dependency (ConfigService) is not available in the created TestingModule.
If you want to mock ForceFailGuard, simply remove it from providers and let .overrideGuard() do its job.
mock_ForceFailGuard had CanActivate as a property instead of canActivate.
Working example (nestjs v6.5.0):
import { CanActivate } from '#nestjs/common';
import { Test, TestingModule } from '#nestjs/testing';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ForceFailGuard } from './force-fail.guard';
describe('AppController', () => {
let appController: AppController;
beforeEach(async () => {
const mock_ForceFailGuard: CanActivate = { canActivate: jest.fn(() => true) };
const app: TestingModule = await Test
.createTestingModule({
controllers: [AppController],
providers: [
AppService,
],
})
.overrideGuard(ForceFailGuard).useValue(mock_ForceFailGuard)
.compile();
appController = app.get<AppController>(AppController);
});
describe('root', () => {
it('should return "Hello World!"', () => {
expect(appController.getHello()).toBe('Hello World!');
});
});
});
If you ever need/want to unit test your custom guard implementation in addition to the controller unit test, you could have something similar to the test below in order to expect for errors etc
// InternalGuard.ts
#Injectable()
export class InternalTokenGuard implements CanActivate {
constructor(private readonly config: ConfigService) {
}
public async canActivate(context: ExecutionContext): Promise<boolean> {
const token = this.config.get("internalToken");
if (!token) {
throw new Error(`No internal token was provided.`);
}
const request = context.switchToHttp().getRequest();
const providedToken = request.headers["authorization"];
if (token !== providedToken) {
throw new UnauthorizedException();
}
return true;
}
}
And your spec file
// InternalGuard.spec.ts
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [],
providers: [
InternalTokenGuard,
{
provide: ConfigService,
useValue: {
get: jest.fn((key: string) => {
if (key === "internalToken") {
return 123;
}
return null;
})
}
}
]
}).compile();
config = module.get<ConfigService>(ConfigService);
guard = module.get<InternalTokenGuard>(InternalTokenGuard);
});
it("should throw UnauthorizedException when token is not Bearer", async () => {
const context = {
getClass: jest.fn(),
getHandler: jest.fn(),
switchToHttp: jest.fn(() => ({
getRequest: jest.fn().mockReturnValue({
headers: {
authorization: "providedToken"
}
})
}))
} as any;
await expect(guard.canActivate(context)).rejects.toThrow(
UnauthorizedException
);
expect(context.switchToHttp).toHaveBeenCalled();
});
I have imported APP_INITIALIZER in my app.module.ts file and calling a initializer function.
How to write unit test case for this initializer method using jasmine/karma?
Code:
initializer.ts
import { CookieService } from 'ngx-cookie-service';
import { KeycloakOptions, KeycloakConfig } from 'keycloak-angular';
import { EnlivenKeycloakAuthService } from './services/enliven-keycloak-auth.service';
export function initializer(keycloakService: EnlivenKeycloakAuthService, cookieService: CookieService): () => Promise<any> {
cookieService.set('test','test');
const config: KeycloakConfig = {
url: serverUrl,
realm: realmName,
clientId: clientId,
credentials: {
secret: secret
}
};
const options: KeycloakOptions = {
config: config,
initOptions: {
onLoad: 'login-required',
checkLoginIframe: false
},
enableBearerInterceptor: true,
bearerExcludedUrls: ['/assets']
};
keycloakService.setIsClientSecretKeyFetech(true);
return (): Promise<any> => keycloakService.init(options);
}
app.module.ts
{
provide: APP_INITIALIZER,
useFactory: initializer,
deps: [ EnlivenKeycloakAuthService, CookieService],
multi: true
}
The code in your question is not clear to me and I couldn't help you with the test case for it, but I'm sure this example will help you.
app-initializer.ts
import { ConfigService } from './services';
export const appInitializer: (ConfigService) => (() => Promise<boolean>) = function (config: ConfigService) {
return (): Promise<boolean> => {
return new Promise<boolean>((resolve: (boolean) => void, reject: (boolean) => void) => {
config.load().subscribe(
(data: any) => {
// do something with data and resolve the promise
resolve(true);
},
(error) => reject(error)
);
});
};
};
app-initializer.spec.ts
import { async, TestBed } from "#angular/core/testing";
import { of } from "rxjs";
import { tap } from "rxjs/operators";
import { ConfigService } from "./services";
import { appInitializer } from "./app-initializer";
describe('appInitializer', () => {
const mockedConfigService: any = jasmine.createSpyObj('ConfigService', ['load']);
let configService: ConfigService;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
{ provide: ConfigService, useValue: mockedConfigService }
]
});
configService = TestBed.get(ConfigService);
});
it('should resolve the promise', async(() => {
mockedConfigService.load.and.returnValue(of({}));
appInitializer(configService)()
.then((status) => {
expect(status).toBe(true);
})
}));
it('should reject the promise', async(() => {
mockedConfigService.load.and.returnValue(
of({})
.pipe(tap(() => {
throw Error('failure');
}))
);
appInitializer(configService)()
.then(() => { }, (error) => {
expect(error.message).toEqual('failure');
})
}));
});