I have a basic serverless application below, I want to test using Jest if getUserById method is called. I am also using inversifyjs. Now when I run my test I am getting an error TypeError: Reflect.hasOwnMetadata is not a function. Another thing how can I mock a response here?
handler.spec.ts
import { IUsersHandler } from './../src/IUsersHandler';
import { UsersHandler } from './../src/UsersHandler';
import { APIGatewayProxyResult } from 'aws-lambda';
let handler: IUsersHandler;
let mockResponse: APIGatewayProxyResult;
describe("UsersHandler", () => {
beforeEach(() => {
handler = new UsersHandler();
});
it("should call getUserById method", () => {
const spy = jest.spyOn(handler, 'getUserById').mockImplementation(async () => mockResponse);
expect(spy).toBeCalledTimes(1);
});
});
UsersHandler Class
import { IUsersHandler } from './IUsersHandler';
import { injectable } from "inversify";
import { APIGatewayProxyHandler, APIGatewayProxyResult, APIGatewayProxyEvent } from "aws-lambda";
#injectable()
export class UsersHandler implements IUsersHandler {
constructor() { }
public getUserById: APIGatewayProxyHandler = async (
event: APIGatewayProxyEvent
): Promise<APIGatewayProxyResult> => {
try {
return {
statusCode: 200,
body: JSON.stringify(event)
};
} catch (err) {
return {
statusCode: 500,
body: JSON.stringify(err)
};
}
};
}
User Interface
import { APIGatewayProxyHandler } from 'aws-lambda';
export interface IUsersHandler {
getUserById: APIGatewayProxyHandler;
}
export const TUsersHandler = Symbol.for('IUsersHandler');
Handler.ts
import { IUsersHandler, TUsersHandler } from './src/IUsersHandler';
import { container } from "./src/inversify.config";
import 'source-map-support/register';
export const getUserById = async function (event, context, callback) {
const handler: IUsersHandler = container.get<IUsersHandler>(TUsersHandler);
return handler.getUserById(event, context, callback);
};
Final handler.spec.ts
import "reflect-metadata";
import { IUsersHandler } from "./../src/IUsersHandler";
import { UsersHandler } from "./../src/UsersHandler";
import { APIGatewayProxyEvent, APIGatewayProxyResult } from "aws-lambda";
let handler: IUsersHandler;
let mockEvent: APIGatewayProxyEvent;
let mockResponse: APIGatewayProxyResult;
describe("UsersHandler", () => {
beforeEach(() => {
mockResponse = {
statusCode: 200,
body: "This is a test",
};
handler = new UsersHandler();
});
it("should call getUserById method", async () => {
const spy = jest
.spyOn(handler, "getUserById")
.mockImplementation(async () => mockResponse);
const response: any = await handler.getUserById(mockEvent, null, null);
expect(spy).toBeCalledTimes(1);
expect(response.body).toBe("This is a test");
expect(response.statusCode).toBe(200);
});
});
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?
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');
});
});
I'm struggling to mock two dependencies from my userService.
Here I have two dependencies: UserRepository and ProfileService
user.service.ts
import { Injectable, NotFoundException, BadRequestException } from '#nestjs/common';
import { UserRepository } from '../repository/user.repository';
import { userDTO } from '../typings/user.typings';
import { ProfileService } from './profile.service';
import { InjectRepository } from '#nestjs/typeorm'
#Injectable()
export class UserService {
constructor(
#InjectRepository(UserRepository)
private readonly repository: UserRepository,
private readonly profileService: ProfileService
) {
this.repository = repository;
}
async addUser(user: userDTO): Promise<userDTO> {
try {
const userCreated = await this.repository.addUser(user);
await this.profileService.createProfile(userCreated)
return userCreated
}
catch (error) {
console.log(error)
}
}
async getUser(field: string, value: string) {
const user = await this.repository.getUser(field, value);
return user
}
async deleteUser(field: string, value: string) {
await this.profileService.deleteProfile(userId)
return await this.repository.deleteUser(userId);
}
async getUserById(userId: string) {
return await this.repository.getUserById(userId);
}
async updateUser(user: userDTO, userId: string) {
return await this.repository.updateUser(user, userId);
}
}
user.service.spec.ts
import { Test, TestingModule } from "#nestjs/testing";
import { UserService } from "../../src/services/user.services";
import { ProfileService } from "../../src/services/profile.service";
import { UserRepository } from "../../src/repository/user.repository";
import { getRepositoryToken } from '#nestjs/typeorm';
describe('userService', () => {
let userService: UserService;
let mockRepository = {};
let mockProfileService = {};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
UserService,
{
provide: getRepositoryToken(UserRepository),
useValue: mockRepository
},
{
provide: ProfileService,
useValue: mockProfileService
},
],
})
.compile();
userService = module.get<UserService>(UserService);
});
it('Should be defined', () => {
expect(userService).toBeDefined();
});
});
The test works fine when I only use UserRepository, but when I tried to mock the second repository it fails with this message
FAIL tests/services/user.service.spec.ts
● Test suite failed to run
Cannot find module 'src/models/profile.models' from '../src/repository/profile.repository.ts'
Can someone explain me how to mock 2 dependencies(a repository and a service).
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();
});