Need to write a unit test for service on Jest - unit-testing

Help me fix the error, and if possible improve the test itself, because it will be based on further tests for services.
I hope for your help
This is my service file
import { Injectable } from '#nestjs/common';
import { InjectRepository } from '#nestjs/typeorm';
import { createQueryBuilder, getConnection, getRepository, Repository, getManager, Connection } from 'typeorm';
import { Brands } from './brands/brands.entity';
import { Categories } from './categories/categories.entity';
import { Volumes } from './volumes/volumes.entity';
import { Packages } from './packages/packages.entity';
import { Tags } from './tags/tags.entity';
import { Countries } from './countries/countries.entity';
import { Sub_categories } from './sub_categories/sub_categories.entity';
import { Products } from './products.entity';
import { IProduct } from './products.interface';
import { Collections } from './collections/collections.entity';
#Injectable()
export class ProductsService {
constructor(
#InjectRepository(Products)
private productRepository: Repository<Products>,
) {}
private readonly products: IProduct[] = [];
async findAllProductsByCategory(category: string): Promise<IProduct[]> {
const qb = getRepository(Products).createQueryBuilder("products");
const subQuery: string = qb.subQuery()
.select('categories.id', 'cat_id')
.from(Categories, 'categories')
.where(`cat_id = '${ category }'`)
.getQuery()
return await this.findAllProductsByArgument( 'category_id', subQuery );
}
async findAllProductsByArgument(argumentId1: string, subQuery1: string, argumentId2?: string, subQuery2?: string): Promise<IProduct[]> {
const qb = getRepository(Products).createQueryBuilder("products");
qb
.select(
`*,
Products.brand_id AS brand_id`
)
.addSelect('brands.brand_name', 'brand_name')
.addSelect('brands.brand_id', 'brand_nameid')
.addSelect('categories.cat_name', 'cat_name')
.addSelect('categories.cat_id', 'cat_id')
.addSelect('volumes.volume_name', 'volume_name')
.addSelect('volumes.volume_id', 'volume_id')
.addSelect('tags.name', 'tag_name')
.addSelect('tags.tag_id', 'tag_nameId')
.addSelect('sub_categories.subcat_name', 'subcategory_name')
.addSelect('sub_categories.subcat_id', 'subcategory_id')
.addSelect('packages.package_name', 'package_name')
.addSelect('packages.package_id', 'package_nameId')
.addSelect('countries.country_name', 'country_name')
.addSelect('countries.country_id', 'country_nameId')
.addSelect('Products.price', 'price_by_one')
.innerJoin(Brands, 'brands', 'Products.brand_id = brands.id')
.innerJoin(Categories, 'categories', 'Products.category_id = categories.id')
.innerJoin(Volumes, 'volumes', 'Products.volume_id = volumes.id')
.innerJoin(Tags, 'tags', 'Products.tag_id = tags.id')
.innerJoin(Sub_categories, 'sub_categories', 'Products.sub_category_id = sub_categories.id')
.innerJoin(Packages, 'packages', 'Products.package_id = packages.id')
.innerJoin(Countries, 'countries', 'Products.country_id = countries.id')
.where('Products.stock > Products.pack_quantity AND isshow = true')
.andWhere(`Products.${ argumentId1 } = ${ subQuery1 }`);
if(argumentId2 && subQuery2){
qb.andWhere(`Products.${ argumentId2 } = ${ subQuery2 }`)
}
return qb.getRawMany();
}
}
I don't really understand how to mocking data and simulate functions
import { Test, TestingModule } from '#nestjs/testing';
import { getRepositoryToken } from '#nestjs/typeorm';
import { Repository } from 'typeorm';
import { Products } from './products.entity';
import { ProductsService } from './products.service';
class ProductsFake {
public async find(): Promise<void> {}
}
describe('ProductsService', () => {
let service: ProductsService;
let repo: Repository<Products>;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
ProductsService,
{
provide: getRepositoryToken(Products),
useClass: ProductsFake
}
]
}).compile();
service = module.get(ProductsService);
repo = module.get(getRepositoryToken(Products));
});
describe('finding a products', () => {
it('should be defined', async () => {
expect(service).toBeDefined();
});
it('return the products list', async () => {
const product = new Products();
const mockData = {
id: 10,
brand_name: "Brewdog",
brand_nameid: "brewdog",
cat_id: "pivo"
};
Object.assign(product, mockData);
const productsRepositoryFindAll = jest
.spyOn(repo, 'find')
.mockResolvedValue([product]);
const result = await service.findAllProductsByCategory( 'pivo' );
expect(result[0]).toBe(product);
expect(productsRepositoryFindAll).toBeCalled()
});
});
});
This is an error that appears when trying to test
йййййййййййййййййййййййййййййййййййййййййййййййййййййййййййййййййййййййййййййййй

Related

How to mock an inject repository with Jest and NestJS?

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?

Mock two dependencies with NestJs and Jest

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).

Testing AWS Severless application using Jest

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);
});
});

Unit testing function in a class that injects EntityManager

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.

Unit Test in NestJs using Jest

So, I want to test a Controller, but I always get the same error, and I not really familiarized with Jest.
I work with php and I already did some unit tests in phpUnit, but with Jest I'm really having some trouble to understand how to do.
Here is my error message
● Test suite failed to run
SyntaxError: /home/webjump-nb103/Projects/development/workspace/stare/customer/src/customer/customer.controller.spec.ts: Unexpected token, expected ";" (13:26)
11 |
12 | describe('Customer Controller', () => {
> 13 | let customerController: CustomerController;
| ^
14 | let customerService: CustomerService;
15 |
16 | beforeAll(async () => {
at Parser.raise (node_modules/#babel/parser/src/parser/location.js:41:63)
at Parser.unexpected (node_modules/#babel/parser/src/parser/util.js:150:16)
at Parser.semicolon (node_modules/#babel/parser/src/parser/util.js:123:40)
at Parser.parseVarStatement (node_modules/#babel/parser/src/parser/statement.js:703:10)
at Parser.parseStatementContent (node_modules/#babel/parser/src/parser/statement.js:216:21)
at Parser.parseStatement (node_modules/#babel/parser/src/parser/statement.js:146:17)
at Parser.parseBlockOrModuleBlockBody (node_modules/#babel/parser/src/parser/statement.js:865:25)
at Parser.parseBlockBody (node_modules/#babel/parser/src/parser/statement.js:841:10)
at Parser.parseBlock (node_modules/#babel/parser/src/parser/statement.js:818:10)
at Parser.parseFunctionBody (node_modules/#babel/parser/src/parser/expression.js:1964:24)
My controller
import {Controller, HttpStatus, Post, Res, Body, ValidationPipe, Put, Param, Get} from '#nestjs/common';
import {CreateCustomerDto} from './dto/customer/customer.dto';
import {CustomerService} from './customer.service';
#Controller('customer')
export class CustomerController {
constructor(private readonly customerService: CustomerService) { }
#Post()
async createPost(#Res() res, #Body(ValidationPipe) createCustomerDto: CreateCustomerDto ) {
const customer = await this.customerService.createCustomer(createCustomerDto);
return res.status(HttpStatus.OK).json({
customer: customer
});
}
#Put('update/:id')
async createUpdate(#Param('id') id: string, #Res() res, #Body(ValidationPipe) createCustomerDto: CreateCustomerDto) {
const customer = await this.customerService.updateCustomer(createCustomerDto, id);
return res.status(HttpStatus.OK).json({
customer: customer
});
}
}
**My Service **
import {Injectable} from '#nestjs/common';
import {Model} from 'mongoose';
import {InjectModel} from '#nestjs/mongoose';
import {Customer} from './interfaces/customer.interface';
import {CreateCustomerDto} from './dto/customer/customer.dto';
#Injectable()
export class CustomerService {
constructor(#InjectModel('customer') private readonly customerModel: Model<Customer>) {}
async createCustomer(createCustomerDto: CreateCustomerDto ): Promise<Customer> {
const customer = new this.customerModel(createCustomerDto);
return customer.save();
}
async updateCustomer(createCustomerDto: CreateCustomerDto, id: string): Promise<Customer> {
const customer = await this.customerModel
.findByIdAndUpdate(id, createCustomerDto, {new: true});
return customer;
}
}
And my test
import { Test, TestingModule } from '#nestjs/testing';
import { CustomerController } from './customer.controller';
import {CustomerService} from './customer.service';
import {CreateCustomerDto} from './dto/customer/customer.dto';
import {Customer} from './interfaces/customer.interface';
import {InjectModel} from '#nestjs/mongoose';
jest.mock('./customer.service.ts');
jest.mock('./customer.controller.ts');
jest.mock('./interfaces/customer.interface.ts');
describe('Customer Controller', () => {
let customerController: CustomerController;
let customerService: CustomerService;
beforeAll(async () => {
const module = await Test.createTestingModule({
controllers: [CustomerController],
providers: [CustomerService],
}).compile();
customerController = module.get(CustomerController);
customerService = module.get<CustomerService>(CustomerService);
});
describe('Create Post', () => {
const dto = new CreateCustomerDto();
const res = 200;
const customerModel: Customer;
it('should return an collection of customer when created ', async () => {
const expectedResult = new CustomerService(#InjectModel(customerModel<Customer>));
jest.spyOn(customerService, 'createCustomer').mockResolvedValue(customerModel);
expect(await customerController.createPost(res, dto)).toBe(expectedResult);
});
});
});
**Any thoughts ?**
As already mentioned in the comments, make sure you have your jest properly configured. Try to add/modify the following to your package.json
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".spec.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}