Mock Router and Ngzone in Angular 7 Jest Unit test - jestjs

I used ngzone and router in component class.
My unit testing was working fine but now getting error cannot read toLowerCase() of undefined.
Can anyone suggest me how to mock both Ngzone and Router.

I use TestBed for this :)
Just configure your test module with your component and routes, then get your Router instance from the test module, and then you spy on Router.navigate to use your custom mock implementation.
import { async, ComponentFixture, TestBed } from '#angular/core/testing';
import { Router } from '#angular/router';
import { RouterTestingModule } from '#angular/router/testing';
let component: YourComponent;
let fixture: ComponentFixture<YourComponent>;
let router: Router;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
RouterTestingModule.withRoutes([
{ path: "", component: YourComponent },
{ path: "**", redirectTo: "" },
]),
],
declarations: [YourComponent],
providers: [],
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(YourComponent);
component = fixture.componentInstance;
router = TestBed.inject(Router);
jest
.spyOn(router, 'navigate')
.mockImplementation(() => of(true).toPromise());
});

Related

NestJS testing that interception has been called on a controller

I'm looking to see if there is a recommended way to write a JEST test that an Interceptor has been called. In the example below LoggingInterceptor was called? The purpose of test is verify that NestJS Binding interceptors is in place.
import { Controller, Get, UseInterceptors } from '#nestjs/common';
import { AppService } from './app.service';
import { LoggingInterceptor, TransformInterceptor } from './transform.interceptor';
#Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
#UseInterceptors(LoggingInterceptor)
#Get()
getHello(): string {
return this.appService.getHello();
}
}```
I would advise against testing that. Think about what you're testing: you're testing that the framework is doing what it says it will, something that the framework itself already tests. Sure you could use the Reflect API and verify that that metadata does exist, but that's something the framework should assert, it's a feature you should just be able to use with peace of mind.
One option I used to create a Jest test to verify that a binding interceptor(Which transformed the response) on a controller was called and produced the expected response was by using NestJS Supertest lib to simulate an end to end test.
Related NestJS doc:
List item
https://docs.nestjs.com/fundamentals/testing#end-to-end-testing
Test Code Sample:
import { INestApplication } from '#nestjs/common';
import { Test } from '#nestjs/testing';
import * as request from 'supertest';
import { CatsModule } from '../../src/cats/cats.module';
import { CatsService } from '../../src/cats/cats.service';
import { CoreModule } from '../../src/core/core.module';
describe('Test interceptor response binding was triggerred', () => {
const aServiceResponse = { findAll: () => ['test'] };
const expectedResponseAfterTransformation = { code: 200, data: [ 'test' ] };
let app: INestApplication;
beforeAll(async () => {
const moduleRef = await Test.createTestingModule({
imports: [CatsModule, CoreModule],
})
.overrideProvider(CatsService)
.useValue(aServiceResponse)
.compile();
app = moduleRef.createNestApplication();
await app.init();
});
it(`/GET cats returns transformed data respsone`, () => {
return request(app.getHttpServer()).get('/cats').expect(200).expect({
data: expectedResponseAfterTransformation,
});
});
afterAll(async () => {
await app.close();
});
});

NestJS Jest error: TypeError: Cannot read properties of undefined (reading '[any variable from required config]')

While trying to cover out project in unit tests using nest's jest I've bumped into a problem of a testing module not being able to pull variables from config.
Basically, I have an EmailService, I want to test it, I use it as a Provider in my testing module. Naturally, as EmailService takes ConfigService in its constructor to pull some variables from config (that initially come from env) I put ConfigService into the providers array as well... well, then upon initialization testing module drops
NestJS Jest error: TypeError: Cannot read properties of undefined (reading 'region')
note: region variable is taken from env in a registered config module
code example of my test that throws
describe('EmailService', () => {
let emailService: EmailService;
let configService: ConfigService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [EmailService, ConfigService],
}).compile();
emailService = module.get<EmailService>(EmailService);
configService = module.get<ConfigService>(ConfigService);
});
it('should be defined', () => {
expect(emailService).toBeDefined();
});
});
I have came to the conclusion that it throws an error specifically because EmailService takes ConfigService in it's constructor in this way:
export class EmailService {
private readonly config: IAwsConfig;
private readonly region: IRegion;
constructor(private readonly configService: ConfigService) {
this.config = this.configService.get('aws');
this.region = this.config.region;
}
aditional info: both EmailService and ConfigService work just fine during a normal runtime, it only fails during jest testing
seems like this.configService.get method returns 'undefined' during a test run and i'm, not sure why or how to fix it. Any ideas?
In case you don't want to import the entire ConfigService but just the config values themselves, then you use them in the test as follows :)
// my-config.ts
import { registerAs } from '#nestjs/config';
export default registerAs('myConfig', () => ({ propA: 'aa', propB: 123 }));
import { Inject } from '#nestjs/common';
import { ConfigType } from '#nestjs/config';
import myConfig from './my-config.ts';
export class EmailService {
private propA: string;
private propB: number;
constructor(
#Inject(myConfig.KEY) config: ConfigType<typeof myConfig>
) {
this.propA = config.propA;
this.propB = config.propB;
}
}
import { ConfigModule, registerAs } from '#nestjs/config';
import { Test, TestingModule } from '#nestjs/testing';
describe('Test', () => {
const configValues = { propA: 'aa', proprB: 123 };
const config = registerAs('testConfig', () => configValues);
beforeEach(async () => {
const app: TestingModule = await Test.createTestingModule({
imports: [ConfigModule.forFeature(config)],
providers: [EmailService],
}).compile();
});
});
Was not able to find an answer for 2 hours straight, but then, 10 minutes after asking a question, there you go, an answer.
Seems like ConfigService doesn't provide configs during jest testing so you have to provide it in the testing module with replaced get method, something like such:
providers: [
EmailService,
{
provide: ConfigService,
useValue: {
get: jest.fn((key: string) => {
return hardcodedConfigFromWithinTheTestFile;
}),
},
},
],

Mock nestjs decorator

I am using a custom Firewall decorator that provides some utility functionality for many of my endpoints (e.g. throttling, authorization etc.) and I want be able to mock this decorator in my endpoints:
#Controller('some')
export class SomeController {
#Firewall() // mock it and check that it's called with correct arguments
async testEndpoint() {
return 'test';
}
}
I want to mock it and check that it's called with the correct parameters, but I can't figure out how I can do this in my test cases:
import * as request from 'supertest';
import { Test } from '#nestjs/testing';
import { INestApplication } from '#nestjs/common';
import { AppModule } from 'src/app.module';
describe('Some Controller', () => {
let app: INestApplication;
beforeAll(async () => {
const moduleRef = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleRef.createNestApplication();
await app.init();
});
it('some testcase', () => {
// What do I do here to mock my Firewall decorator? // <--- <--- <---
return request(app.getHttpServer()).get('/some/testEndpoint').expect(401);
});
afterAll(async () => {
await app.close();
});
});
If it can help, here is a short version of the Firewall decorator:
import { applyDecorators } from '#nestjs/common';
import { Throttle, SkipThrottle } from '#nestjs/throttler';
export function Firewall(options?: { skipThrottle?: boolean }) {
const { skipThrottle } = options || {
anonymous: false,
};
const decorators = [];
if (skipThrottle) {
decorators.push(SkipThrottle());
} else {
decorators.push(Throttle(10, 10));
}
return applyDecorators(...decorators);
}
I have checked other answers (including this one) but they didn't help.
Thanks in advance for your time!
The #Throttle() and #SkipThrottle() decorators only apply metadata to the controller / controller method they decorate. They don't do anything on their own. Your custom #Firewall() is a utility decorator to combine these into a single decorator for convenience.
If you take a look at the source code of the nestjs/throttler package you'll see it is the #ThrottlerGuard() guard that retrieves this metadata and actually does the throttling.
I suspect you configured this one as a global app guard, so it is applied for all requests.
#Module({
imports: [
ThrottlerModule.forRoot({...}),
],
providers: [
{
provide: APP_GUARD,
useClass: ThrottlerGuard,
},
],
})
export class AppModule {}
In your test you need to mock the ThrottlerGuard.
const ThrottlerGuardMock = {
canActivate(ctx) {
const request = ctx.switchToHttp().getRequest();
// mock implementation goes here
// ...
return true;
}
} as ThrottlerGuard;
const module = await Test.createTestModule({
imports: [AppModule]
})
.overrideProvider(ThrottlerGuard)
.useValue(ThrottlerGuardMock) // <-- magic happens here
.compile();
app = moduleRef.createNestApplication();
await app.init();
You could setup some spies, in the mocked guard retrieve the metadata set by the decorators applied by the #Firewall() decorator and then invoke the spies with that metadata. Then you could just verify if the spies were called with the expected values. That would verify that your custom decorator passed down the expected values to the throttle decorators. Actually testing the #ThrottleGuard() decorator is the nestjs/throttler package's responsibility.

NestJs Testing - Is REPOSITORY part of the relevant providers/imports within module

I am trying to write the unit test cases for the mono-repo based application using nestjs.
I'm facing the following error
Nest cannot export a provider/module that is not a part of the currently processed module (CacheManagerModule). Please verify whether the exported CACHE_MANAGER_REPOSITORY is available in this particular context.
Possible Solutions:
- Is CACHE_MANAGER_REPOSITORY part of the relevant providers/imports within CacheManagerModule?
Used Packages as follows
"#nestjs/testing": "6.11.7",
"jest": "25.1.0",
"ts-jest": "25.0.0"
Please refer the sample code below
apps\api\src\modules\enduse\enduse.controller.spec.ts
import { INestApplication } from "#nestjs/common";
import { Test } from "#nestjs/testing";
import { CacheManagerModule } from "#app/cache-manager";
import * as request from "supertest";
import { EnduseController } from "./enduse.controller";
import { EnduseService } from "./enduse.service";
describe("Enduse", () => {
let app: INestApplication;
beforeAll(async () => {
const module = await Test.createTestingModule({
imports: [CacheManagerModule],
controllers: [EnduseController],
providers: [EnduseService],
}).compile();
app = module.createNestApplication();
await app.init();
});
it("/ (Get Enduse)", async () => {
const response = await request(app.getHttpServer()).get("enduse/mapping?enduse=123").expect(200);
return response;
});
});
libs\cache-manager\src\cache-manager.module.ts
import { Module } from "#nestjs/common";
import { CacheManagerService } from "./cache-manager.service";
import { CacheManagerProviders } from "./cache-manager.provider";
#Module({
providers: [CacheManagerService, ...CacheManagerProviders],
exports: [CacheManagerService, ...CacheManagerProviders],
})
export class CacheManagerModule {}
libs\cache-manager\src\cache-manager.provider.ts
import { ReviewModel } from "#app/database/models";
import { CACHEMANAGERCONST } from "./cache-manager.const";
export const CacheManagerProviders = [
{
provide: CACHEMANAGERCONST.CACHE_MANAGER_REPOSITORY,
useValue: ReviewModel,
},
];
jest.config.js
moduleNameMapper: {
"^#app/cache-manager": resolve(__dirname, "./libs/cache-manager/src")
}
How to solve the issue?
Thanks.

ValidationPipe dont work when use app.useGlobalPipes

Hello. I wanna to use ValidationPipe globaly with useGlobalPipes. I use :
import 'dotenv/config';
import {NestFactory} from '#nestjs/core';
import {ValidationPipe} from '#nestjs/common';
import {AppModule} from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe({
transform: true,
whitelist: true,
}));
await app.listen(3000);
}
bootstrap();
But this dont work. Work only when I add VAlidationPipe in my controler :
#Post('register')
#UsePipes(new ValidationPipe({ transform: true, whitelist: true}))
async register(#Body() userDTO: RegisterDTO) {
const user = await this.userService.create(userDTO);
const payload: Payload = {
userName: user.userName,
seller: user.seller,
};
const token = await this.authService.signPayload(payload);
return {user, token};
}
Use this construction (await app).useGlobalPipes(new ValidationPipe)
I came across the same issue, I found the solution was on https://docs.nestjs.com/pipes#global-scoped-pipes:
Global pipes are used across the whole application, for every controller and every route handler.
Note that in terms of dependency injection, global pipes registered from outside of any module (with useGlobalPipes() as in the example above) cannot inject dependencies since the binding has been done outside the context of any module. In order to solve this issue, you can set up a global pipe directly from any module using the following construction:
import { Module } from '#nestjs/common';
import { APP_PIPE } from '#nestjs/core';
#Module({
providers: [
{
provide: APP_PIPE,
useClass: ValidationPipe,
},
],
})
export class AppModule {}
Having this in your AppModule makes the global validation pipe work.

Resources