I'm trying to write unit tests for my controller in nestjs, here's the source of my controller:
import { Controller, Inject, Logger } from '#nestjs/common'
import { CONTEXT, MessagePattern, RequestContext } from '#nestjs/microservices'
import { CustomersService } from './customer.service'
#Controller("customers")
export class CustomersController {
constructor(
#Inject(CONTEXT)
private ctx: RequestContext,
private readonly customersService: CustomersService
) {}
#MessagePattern("ticketing_customer_actions")
async customersActions(): Promise<RpcResult> {
const data = this.ctx.data;
if (data.customerInfo.expire_date) {
try {
"REST OF THE CODE ....."
data is the where the json comes to my controller from the rabbitmq, so I need to mock this.ctx.data and that's what I do not know how to do. I mocked my service with just giving my service path to the jest.mock() function.
Here's my test so far:
jest.mock("../Customers/customer.service.ts");
describe("CustomersController", () => {
let controller: CustomersController;
let service: CustomersService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [CustomersController],
providers: [CustomersService],
}).compile();
service = module.get<CustomersService>(CustomersService);
controller = module.get<CustomersController>(CustomersController);
});
it("should be defined", () => {
expect(controller).toBeDefined();
});
it("CustomersActions", async () => {
const result = new RpcResult({});
jest
.spyOn(service, "customersActions")
.mockImplementation(async () => result);
expect(await controller.customersActions()).toBe(result);
});
});
Related
I need to switch out my backend in-memory DB for testing due to memory issues. Below is my code
import { fireEvent, render, screen, waitFor } from "#testing-library/react";
import userEvent from "#testing-library/user-event";
import App from "App";
import axios from "axios";
import MockAdapter from "axios-mock-adapter";
import { AccessLevel, ResponseApi, SystemUserApi } from "types";
let mock: MockAdapter;
beforeAll(() => {
mock = new MockAdapter(axios);
});
afterEach(() => {
mock.reset();
});
beforeEach(() => {
jest.resetModules();
});
describe("<App />", () => {
test("login", async () => {
mock.onPost('/Hello').reply(200, getPost);
const result = render(<App />);
const user = userEvent.setup();
const btnLogin = screen.getByText(/Login/i) as HTMLButtonElement;
await userEvent.click(btnLogin);
let btnOk = screen.queryByText(/OK/i) as HTMLButtonElement;
expect(btnOk.disabled).toBe(true);
let btnCancel = screen.getByText(/Cancel/i) as HTMLButtonElement;
expect(btnCancel.disabled).toBe(false);
fireEvent.change(screen.getByLabelText(/Access Code/i) as HTMLInputElement, { target: { value: 'USER' } });
expect(btnOk.disabled).toBe(false);
await userEvent.click(btnOk);
//At this point I was expecting the onPost to be clicked
});
});
function getPost(config: any): any {
console.log(config);
debugger;
return {
data: {
access_code: 'USER'.toUpperCase(),
access_level: AccessLevel.USER ,
lock_level:true
} as SystemUserApi,
error: false,
} as ResponseApi
}
Deep down in the is a call axios post to /Hello but my function within the test is not called. I do not know if it has to do with the actual call being axios.request vs axios.post. I have tried switching to mock.onAny, but that did not seem to work. Not sure what to do here.
I'm having trouble unit testing a prisma.service.ts file:
import { INestApplication, Injectable } from '#nestjs/common';
import { PrismaClient } from '#prisma/client';
#Injectable()
export class PrismaService extends PrismaClient {
async enableShutdownHooks(app: INestApplication) {
this.$on('beforeExit', async () => {
await app.close();
});
}
}
The prisma.service.spec.ts I have currently looks like this:
import { INestApplication } from '#nestjs/common';
import { NestFastifyApplication } from '#nestjs/platform-fastify';
import { Test, TestingModule } from '#nestjs/testing';
import { PrismaService } from './prisma.service';
const MockApp = jest.fn<Partial<INestApplication>, []>(() => ({
close: jest.fn(),
}));
describe('PrismaService', () => {
let service: PrismaService;
let app: NestFastifyApplication;
beforeEach(async () => {
app = MockApp() as NestFastifyApplication;
const module: TestingModule = await Test.createTestingModule({
providers: [PrismaService],
}).compile();
service = module.get<PrismaService>(PrismaService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
describe('enableShutdownHooks', () => {
it('should call $on and successfully close the app', async () => {
const spy = jest.spyOn(PrismaService.prototype, '$on')
.mockImplementation(async () => {
await app.close();
});
await service.enableShutdownHooks(app);
expect(spy).toBeCalledTimes(1);
expect(app.close).toBeCalledTimes(1);
spy.mockRestore();
});
});
});
However, this does not test line 8 of prisma.service.ts:
await app.close();
because I am mocking the implementation of this.$on('beforeExit', callback), with a copy of its original implementation.
Even if I don't mock it, app.close() never gets called.
Is there a way to test this line?
Could you try using a callback:
jest
.spyOn(service, '$on')
.mockImplementation(async (eventType, cb) => cb(() => Promise.resolve()))
await service.enableShutdownHooks(app);
expect(service.$on).toBeCalledTimes(1);
That allows you to use the callback to invoke the function where await app.close() is located.
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.
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();
});