Unit Test in NestJs using Jest - unit-testing

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"
}

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 objection model dependecy in NestJs with Jest

I'm trying to make unit test with nestjs and objection. The problem I have is that I can't mock the "User" Model that is injected with the decorator "#InjectModel". I searched a lot to find a solution but I didn't find anything.
users.service.ts
import { HttpException, HttpStatus, Inject, Injectable } from '#nestjs/common';
import { CreateUserDto } from './create-user.dto';
import { User } from 'src/app.models';
import { InjectModel } from 'nestjs-objection';
#Injectable()
export class UsersService {
constructor(
#InjectModel(User) private readonly userModel: typeof User,
) {}
async create(createUserDto: CreateUserDto) {
try {
const users = await this.userModel.query().insert(createUserDto);
return users
} catch (err) {
console.log(err)
throw new HttpException(err, HttpStatus.BAD_REQUEST);
}
}
}
users.service.spec.ts
import { Test, TestingModule } from "#nestjs/testing";
import { UsersService } from "../src/users/users.service";
import { CreateUserDto } from "src/users/create-user.dto";
import { User } from "../src/app.models";
import { getObjectionModelToken } from 'nestjs-objection';
describe('userService', () => {
let userService: UsersService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
UsersService,
{
provide: User,
useValue: {}
},
],
}).compile();
userService = module.get<UsersService>(UsersService);
});
it('Should be defined', () => {
expect(userService).toBeDefined();
});
it('Should add pin to a created user', async () => {
const createUserDTO: CreateUserDto = {
email: 'mockEmail#mock.com',
userName: 'user'
}
const res = await userService.create(createUserDTO)
expect(res).toHaveProperty('pin')
});
I tried to use import { getObjectionModelToken } from 'nestjs-objection'; inside provider like this:
providers: [
UsersService,
{
provide: getObjectionModelToken(User),
useValue: {}
},
],
I got this error
It asks for a "connection" but I don't know what to put on it.
I suppose "getObjectionModelToken" is the function to mock the "InjectModel". When I pass an empty string
I got this error:
● Test suite failed to run
Cannot find module 'src/app.models' from '../src/users/users.service.ts'
Require stack:
C:/nestjs-project/nestjs-knex/src/users/users.service.ts
users.repository.spec.ts
1 | import { HttpException, HttpStatus, Inject, Injectable } from '#nestjs/common';
2 | import { CreateUserDto } from './create-user.dto';
> 3 | import { User } from 'src/app.models';
| ^
4 | import {
5 | InjectModel,
6 | synchronize,
at Resolver._throwModNotFoundError (../node_modules/jest-resolve/build/resolver.js:491:11)
at Object.<anonymous> (../src/users/users.service.ts:3:1)
If I change the path it breaks the correct functionality of the app
That looks like an error from jest not understanding what src/* imports are. Either use relative imports rather than absolute (e.g. use import { User } from '../app.models') or tell jest how to resolve src/* imports via the moduleNameMapper in your jest.config.js or package.json
{
"moduleNameMapper": {
"^src/(.*)$": "<rootDir>/path/to/src/$1"
}
}
I think based on the error your /path/to/src should be ../src but I'm not 100% sure, so make sure you set that correctly.

NestJs unit testing error: TypeError: this.x is not a function

I have a small issue with a unit test I wrote for a controller method.
Short version:
return this.userPreferencesService.createPreferences(eUserId, userPreferences);
Long version:
I get this error:
UserPreferencesController › createUserPreferences › should create a new userPreferences
TypeError: this.userPreferencesService.createPreferences is not a function
31 | userPreferences: TestUserPreferencesDto,
32 | ): Promise<UserPreferences> {
> 33 | return this.userPreferencesService.createPreferences(eUserId, userPreferences);
| ^
34 | }
35 |
36 | /**
at UserPreferencesController.createPreferences (user-preferences/user-preferences.controller.ts:33:44)
at Object.<anonymous> (user-preferences/user-preferences.controller.spec.ts:67:45)
The toBeDefined passes but the createUserPreferences fails for the error above.
The code works great and there only the test fails.
I just can't find the reason this is not a function?
service file content (relevant data only):
#Injectable()
export class UserPreferencesService {
constructor(#InjectModel('UserPreferences') private userPreferencesModel: Model<UserPreferences>) {
}
/**
* ADD a single user preferences by id => POST api/v1/user-preferences/
* #param userPreferences
*/
async createPreferences(eUserId: string, userPreferences: TestUserPreferencesDto): Promise<UserPreferences> {
Object.assign(userPreferences, {eUserId: eUserId});
return this.userPreferencesModel.create(userPreferences);
}
This is the controller (relevant data only)
#Controller('v1/user-preferences')
export class UserPreferencesController {
constructor(private userPreferencesService: UserPreferencesService) {}
/**
* Add user preferences to the database
* #param userPreferences
*/
#Post()
async createPreferences(
#Headers('x-e-user-id') eUserId: string,
#Body()
userPreferences: TestUserPreferencesDto,
): Promise<UserPreferences> {
return this.userPreferencesService.createPreferences(eUserId, userPreferences);
}
This is the entire test file:
import {Test, TestingModule} from '#nestjs/testing';
import { getModelToken } from '#nestjs/mongoose';
import { Model } from 'mongoose';
import {UserPreferencesController} from './user-preferences.controller';
import {UserPreferencesService} from './user-preferences.service';
import { exitToOptions } from './schemas/user-preferences.schema';
const ReturnedUserPreferencesMock = {
_id: '62a161a9654a511b28e6f3db',
eUserId: '123456',
uiTheme: 'dark',
panelWidth: 300,
editingHandles: true,
enableLightboxInEditor: true,
hiddenElements: true,
defaultDeviceView: 'mobile',
exitTo: exitToOptions.DASHBOARD,
};
const eUserIdeMock = '123456';
const userPreferencesMock = {
uiTheme: 'dark',
panelWidth: 327,
editingHandles: true,
enableLightboxInEditor: true,
hiddenElements: true,
defaultDeviceView: 'mobile',
exitTo: exitToOptions.DASHBOARD,
}
const mockUserPreferencesService = {
create: jest.fn().mockResolvedValueOnce(ReturnedUserPreferencesMock),
}
describe('UserPreferencesController', () => {
let controller: UserPreferencesController;
let service: UserPreferencesService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [UserPreferencesController],
providers: [{
provide: UserPreferencesService,
useValue: mockUserPreferencesService
}]
}).compile();
controller = module.get<UserPreferencesController>(UserPreferencesController);
service = module.get<UserPreferencesService>(UserPreferencesService);
});
it('Controler should be defined', () => {
expect(controller).toBeDefined();
});
describe('createUserPreferences', () => {
it('should create a new userPreferences', async () => {
const result = await controller.createPreferences(eUserIdeMock, userPreferencesMock);
expect(service.createPreferences).toHaveBeenCalledWith(eUserIdeMock, userPreferencesMock);
expect(result).toEqual(ReturnedUserPreferencesMock);
});
});
});
The mockUserPreferencesService you provide does not have a createPreferences method, only a create. You need to provide an implementation for each method you are going to call of the original service for your mock.
const mockUserPreferencesService = {
create: jest.fn().mockResolvedValueOnce(ReturnedUserPreferencesMock),
createPreferences: jest.fn().mockResolvedValueOnce(WhateverObjectShouldGoHere),
}

How to mock an imported function into a test suite in NestJs?

I want to write a unit test for my payment service but I'm receiving this error:
source.subscribe is not a function
at ./node_modules/rxjs/src/internal/lastValueFrom.ts:60:12
This is my service
import { HttpService } from '#nestjs/axios';
import { Injectable } from '#nestjs/common';
import { lastValueFrom } from 'rxjs';
import { PaymentInfo } from 'src/utils/types/paymentInfo';
#Injectable()
export class PaymentsService {
constructor(private readonly httpService: HttpService) {}
private createHeaderWithAuth(auth, contentType = 'application/json') {
return {
headers: {
authorization: auth.replace('Bearer', '').trim(),
'Content-Type': contentType,
},
};
}
async makePayment(auth: string, paymentInfo: PaymentInfo) {
const configs = this.createHeaderWithAuth(auth);
const response = await lastValueFrom(
await this.httpService.post(
`${process.env.PAYMENT_URL}/transaction/pay`,
paymentInfo,
configs
)
).catch((error) => {
console.log(error);
throw new Error(error.response.data.message);
});
return response.data;
}
}
So with a bit of searching and tinkering found out that this is caused by my import of a rxjs function to resolve the observable setted by axios.
I've searched ways to mock this function so I can properly test my service. But none of them gave me a solution, the questions i found only revolved around functions with modules, but these have none since is imported from a third party lib.
This is my test suite:
describe('Payments Service', () => {
let service: PaymentsService;
let mockedHttpService = {
post: jest
.fn()
.mockImplementation(
async (
url: string,
paymentInfo: PaymentInfo,
header = mockedHeader
) => {
return { mockedSuccessfulResponse };
}
),
get: jest
.fn()
.mockImplementation(async (url: string, header = mockedHeader) => {
return { ...mockedSuccessfulResponse, data: mockedUserCards };
}),
};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
PaymentsService,
{
provide: HttpService,
useValue: mockedHttpService,
},
],
}).compile();
service = module.get<PaymentsService>(PaymentsService);
});
describe('Initialize', () => {
it('should define service', () => {
expect(service).toBeDefined();
});
describe('makePayment', () => {
it('should make a payment', async () => {
const payment = await service.makePayment(mockedAuth, mockedPaymentInfo);
expect(mockedHttpService.post).toHaveBeenCalledWith(
`${process.env.PAYMENT_URL}/transaction/pay`,
mockedPaymentInfo,
mockedHeader
);
expect(payment).toBe(mockedSuccessfulResponse);
});
});
});
Ps.: I removed the mocked objects to reduce the amount of code to read
you should use the of operator from rxjs, and drop the async keyword. Like:
.mockImplementation(
(
url: string,
paymentInfo: PaymentInfo,
header = mockedHeader
) => {
return of({ mockedSuccessfulResponse });
}
otherwise lastValueFrom won't receive an observable object.

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.