Nest.js testing cannot override Provider - javascript

I have a problem with overriding provider in nest.js application for testing.
My stats.controller.spec.ts:
import { StatsService } from './services/stats.service';
import { StatsController } from './stats.controller';
describe('StatsController', () => {
let controller: StatsController;
const mockStatsService = {};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [StatsController],
providers: [StatsService],
})
.overrideProvider(StatsService)
.useValue(mockStatsService)
.compile();
controller = module.get<StatsController>(StatsController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
My stats.controller.ts:
import { Controller, Get } from '#nestjs/common';
import { StatsService } from './services/stats.service';
#Controller('stats')
export class StatsController {
constructor(private statsService: StatsService) {}
#Get('weekly')
getWeeklyStats() {
return this.statsService.getWeeklyStats();
}
#Get('monthly')
getMonthlyStats() {
return this.statsService.getMonthlyStats();
}
}
And my stats.service.ts:
import { Injectable } from '#nestjs/common';
import { InjectRepository } from '#nestjs/typeorm';
import { Repository } from 'typeorm';
import { Trip } from 'src/trips/trip.entity';
import { from, map } from 'rxjs';
import { DatesService } from 'src/shared/services/dates.service';
#Injectable()
export class StatsService {
constructor(
#InjectRepository(Trip) private tripRepository: Repository<Trip>,
private datesServices: DatesService,
) {}
//some code here
}
And after running test I get following errors:
Cannot find module 'src/trips/trip.entity' from 'stats/services/stats.service.ts'
I would really appreciate some help.

The error is unrelated to if you're using a custom provider or overrideProvider. Jest by default doesn't understand absolute imports by default, and you need to use the moduleNameMapper option to tell Jest how to resolve src/ imports. Usually something like
{
"moduleNameMapper": {
"^src/(.*)$": "<rootDir>/$1"
}
}
Assuming rootDir has been set to src

You can override the provider by using the below code in nest js:
import { StatsService } from './services/stats.service';
import { StatsController } from './stats.controller';
describe('StatsController', () => {
let controller: StatsController;
const mockStatsService = {};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [StatsController],
providers: [{
provide: StatsService,
useValue: mockStatsService
}],
})
.compile();
controller = module.get<StatsController>(StatsController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});

Related

Why endpoints not working from imported NESTJS module

tell me please why appController working and itemsController no (from imported module)
I learn nestjs and i did it according to documentation. This controller working its uncomment endpoint.
import {Controller, Get} from '#nestjs/common';
import { AppService } from './app.service';
#Controller()
export class AppController {
constructor() {}
// #Get()
// getHome():string {
// return 'Hello world!';
// }
}
itemsController.ts - working if change appController.ts on this
import {Controller, Get, HttpException, HttpStatus, Param, Post, Res} from "#nestjs/common";
import {Response} from "express";
import {ItemsService} from "./items.service";
import {ItemDto} from "./dto/item.dto";
#Controller()
export class ItemsController {
constructor(private readonly itemsService: ItemsService) {}
#Get()
getAll(#Res({passthrough: true}) res: Response):string | object {
const items = this.itemsService.getAll();
if(!!items.length) {
res.status(HttpStatus.OK);
return new HttpException({
items: items
}, HttpStatus.OK).getResponse();
}
res.status(HttpStatus.INTERNAL_SERVER_ERROR);
return new HttpException({
items: 'Items length: ' + items.length,
status: HttpStatus.INTERNAL_SERVER_ERROR
}, HttpStatus.INTERNAL_SERVER_ERROR).getResponse();
}
#Post()
create(#Param() params):ItemDto {
return this.itemsService.create(params);
}
}
Test jest working:
import { Test } from '#nestjs/testing';
import { ItemsController } from './items/items.controller';
import { ItemsService } from './items/items.service';
import * as request from 'supertest';
import { INestApplication } from "#nestjs/common";
describe('ItemsModule', () => {
let itemsController: ItemsController;
let itemsService: ItemsService;
let app: INestApplication;
beforeEach(async () => {
const moduleRef = await Test.createTestingModule({
controllers: [ItemsController],
providers: [ItemsService],
}).compile();
itemsService = moduleRef.get<ItemsService>(ItemsService);
itemsController = moduleRef.get<ItemsController>(ItemsController);
app = moduleRef.createNestApplication();
await app.init();
});
describe('End-points', () => {
it('/GET Status 200', () => {
return request(app.getHttpServer())
.get('/')
.expect(200)
.expect({
"items": [
{
id: '0',
title: '',
message: ''
}
]
});
});
it('/GET Status 500', () => {
return request(app.getHttpServer())
.get('/')
.expect(500)
.expect({
items: 'Items length: 0',
status: 500
});
});
});
});
I pushed all on github, you can see all code
After looking at your source code, you're missing the # for #Module() in your ItemsModule

How to test Auth 0 implemented with passport strategy with Nestjs

I have been working on a feature where the goal is to allow a user to login via Auth0. I am using a passport such as passport-auth0 package to implement it. I was able to get working. However, I am not able to test it. I would like to know how I can test auth/login and auth/callback controllers methods.
Moreover, I would like to understand how to mock #UseGuards(AuthGuard('auth0')) and a middleware since I have used them.
Different ways I have tried I got the following error
[Nest] 23402 - 03/30/2020, 5:45:37 PM [ExceptionHandler] Cannot set property 'authParams' of undefined
**TypeError: Cannot set property 'authParams' of undefined**
at Auth0Strategy.Strategy.authenticate (/Users/directory/node_modules/passport-auth0/lib/index.js:82:28)
at attempt (/Users/directory/node_modules/passport/lib/middleware/authenticate.js:366:16)
at authenticate (/Users/directory/node_modules/passport/lib/middleware/authenticate.js:367:7)
at /Users/directory/node_modules/#nestjs/passport/dist/auth.guard.js:84:3
at new Promise (<anonymous>)
at /Users/directory/node_modules/#nestjs/passport/dist/auth.guard.js:76:83
at MixinAuthGuard.<anonymous> (/Users/directory/node_modules/#nestjs/passport/dist/auth.guard.js:48:36)
at Generator.next (<anonymous>)
at /Users/directory/node_modules/#nestjs/passport/dist/auth.guard.js:20:71
at new Promise (<anonymous>)
✨ Done in 38.11s.
// Auth.module.ts
import { Module, NestModule, MiddlewareConsumer } from '#nestjs/common';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
import { Auth0Strategy } from './auth.strategy';
import { AuthMiddleware } from './ middlewares/auth.middleware'
#Module({
controllers: [AuthController],
providers: [
AuthService,
Auth0Strategy
],
exports: [AuthService],
})
export class AuthModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(AuthMiddleware)
.forRoutes('auth/callback');
}
}
// auth.strategy.ts
import { Injectable, Query } from '#nestjs/common';
import { PassportStrategy } from '#nestjs/passport';
import { Strategy } from 'passport-auth0';
import { AuthService, Provider } from './auth.service';
import { config } from 'dotenv';
config();
#Injectable()
export class Auth0Strategy extends PassportStrategy(Strategy, 'auth0') {
constructor(
private readonly authService:AuthService
)
{
super(
{
domain: process.env.AUTH0_DOMAIN,
clientID: process.env.AUTH0_CLIENT_ID,
clientSecret: process.env.AUTH0_CLIENT_SECRET,
callbackURL: process.env.AUTH0_CALLBACK_URL,
redirectUri: process.env.AUTH0_CALLBACK_URL,
audience: process.env.AUTH0_AUDIENCE,
responseType: 'code',
scope: 'openid profile email',
},
)
}
async validate(request: any, accessToken: string, refreshToken: string, profile, done: Function): Promise<any> {
try {
const jwt: string = await this.authService.validateOAuthLogin(profile, Provider.Auth0);
const user =
{
jwt
}
return done(null, user);
}
catch (err) {
return done(err, false);
}
}
}
//auth.service.ts
import { Injectable, InternalServerErrorException, HttpException } from '#nestjs/common';
import { sign } from 'jsonwebtoken';
export enum Provider {
Auth0 = 'auth0'
}
#Injectable()
export class AuthService {
private readonly JWT_SECRET_KEY = process.env.JWT_SECRET_KEY
async validateOAuthLogin(profile: object, provider: Provider): Promise<string> {
try {
const isProfileExist = Object.entries(profile).length;
if (isProfileExist === 0) {
throw new HttpException('User profile is empty please login again', 400);
}
const payload = {
profile,
provider
}
const jwt: string = sign(payload, this.JWT_SECRET_KEY, { expiresIn: '1h' });
return jwt;
}
catch (err) {
throw new InternalServerErrorException('validateOAuthLogin', err.message);
}
}
}
import { Controller, Get, UseGuards, Res, Req } from '#nestjs/common';
import { AuthGuard } from '#nestjs/passport';
#Controller('auth')
export class AuthController {
#Get('login')
#UseGuards(AuthGuard('auth0'))
auth0Login() {}
#Get('callback')
#UseGuards(AuthGuard('auth0'))
auth0LoginCallback(#Req() req, #Res() res) {
const jwt: string = req.user.jwt;
if (jwt) {
res.redirect(`${process.env.AUTH0_SUCCESS_REDIRECTION_URL}/${jwt}`);
} else {
res.redirect(process.env.AUTH0_FAILURE_REDIRECTION_URL);
}
}
}
// auth.controller.spec.ts
import { Test, TestingModule } from '#nestjs/testing';
import { Auth0Strategy } from '../auth.strategy';
import { AuthModule } from '../auth.module';
import { auth0SuccessRequest, fekeToken } from './auth.mock';
describe('AuthService', () => {
let strategy: Auth0Strategy;
const { request, profile, accessToken, refreshToken } = auth0SuccessRequest;
let done: Function = jest.fn();
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [AuthModule],
providers: [Auth0Strategy]
}).compile();
strategy = module.get<Auth0Strategy>(Auth0Strategy);
});
afterAll(async () => {
jest.clearAllMocks();
});
it('should validate Auth 0 data', async () => {
await strategy.validate(request, accessToken, refreshToken, profile, done)
expect(done).toBeCalledTimes(1);
});
it('should not proceed without a profile function', async () => {
const failuredAuth = await strategy.validate(request, accessToken, refreshToken, {}, done);
expect(failuredAuth).toBeFalsy();
expect(failuredAuth).toBeUndefined();
});
});
// custom-guard.ts
import { ExecutionContext, Injectable, UnauthorizedException } from '#nestjs/common';
import { AuthGuard } from '#nestjs/passport';
#Injectable()
export class CustomGuard extends AuthGuard('auth0') {
canActivate(context: ExecutionContext) {
return super.canActivate(context);
}
handleRequest(err, user, info) {
if (err || !user) {
throw err || new UnauthorizedException();
}
return user;
}
}
use your CustomGuard and test with it
#Controller('auth')
export class AuthController {
#UseGuards(CustomGuard) // use you guard
auth0LoginCallback(#Req() req, #Res() res) {
}
}
Hope it will help you and it is https://docs.nestjs.com/guards documentation
you are right #harut Barseghyan. with the custom guard you shared with me I was able to activate or override guard in the test environment when I needed it.
// auth.controller.ts
import { Controller, Get, UseGuards, Res, Req } from '#nestjs/common';
import { AuthGuard } from '#nestjs/passport';
import { AuthCustomGuard } from '../auth/guards/auth.guard';
#Controller('auth')
export class AuthController {
#Get('login')
// #UseGuards(AuthGuard('auth0')). // I no longer use this guard
#UseGuards(AuthCustomGuard). // I instead use Auth0 custom guards
auth0Login() {}
#Get('callback')
// #UseGuards(AuthGuard('auth0'))
#UseGuards(AuthCustomGuard)
auth0LoginCallback(#Req() req, #Res() res) {
const jwt: string = req.user.jwt;
if (jwt) {
res.redirect(`${process.env.AUTH0_SUCCESS_REDIRECTION_URL}/${jwt}`);
} else {
res.redirect(process.env.AUTH0_FAILURE_REDIRECTION_URL);
}
}
}
Since I want to authorize the Auth0 routes, I still need Auth 0 guards. The snippet code-shared by #harut Barseghyan.
// auth.guard.ts
import { ExecutionContext, Injectable, UnauthorizedException } from '#nestjs/common';
import { AuthGuard } from '#nestjs/passport';
#Injectable()
export class AuthCustomGuard extends AuthGuard('auth0') {
canActivate(context: ExecutionContext) {
return super.canActivate(context);
}
}
The fact I have a custom guard, I have the power to activate it vice versa in a testing environment
// auth.controller.spec.ts
import { Test, TestingModule } from '#nestjs/testing';
import { AuthController } from '../auth.controller';
import { INestApplication } from '#nestjs/common';
import { AuthModule } from '../auth.module';
import * as request from 'supertest';
import { AuthCustomGuard } from '../guards/auth.guard';
describe('Auth Controller', () => {
let app: INestApplication;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [AuthModule],
controllers: [AuthController],
})
.overrideGuard(AuthCustomGuard) // can you see that I override it
.useValue({ canActivate: () => true }) // true helped me to skip
.compile();
app = module.createNestApplication();
app.init()
});
it('should be visit auth login route', async () => {
return request(app.getHttpServer())
.get('/auth/login')
.expect(302)
});
it('should not redirect user to login page if auth 0 throws an error such as an invalid error request', async () => {
return request(app.getHttpServer())
.get('/auth/callback?error=invalid_request')
.expect(302)
});
afterAll(async () => {
jest.clearAllMocks();
await app.close();
});
});

Testing functions of AngularFireAuth with jasmine

how are you?
it's my first time with jasmine, and I'm lost.
I just want to test my method of AngularFireAuth.
I have this function in my service:
emailPasswordLoginAsPromise(login) {
return new Promise((resolveEPL, rejectEPL) => {
this.angularFireAuth.auth.signInWithEmailAndPassword(login.email, login.password)
.then(credential => {
resolveEPL(credential.user);
}).catch(e => {
console.error('emailPasswordLogin', e);
rejectEPL(e);
});
});
}
and I want to test in jasmine, and I have this in my Spec.js:
I know the basic of mocks, but I don't know how to apply this in my test, can anyone help me please? I tried to found some examples, but I got very confused.
import { TestBed, inject } from '#angular/core/testing';
import { AuthService } from './auth.service';
import { AngularFirestore } from '#angular/fire/firestore';
import { AngularFireAuth } from '#angular/fire/auth';
import { AngularFireModule } from '#angular/fire';
import { environment } from 'src/environments/environment';
import { RouterTestingModule } from '#angular/router/testing';
fdescribe('AuthService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
AngularFireModule.initializeApp(environment.firebase),
RouterTestingModule
],
providers: [
AuthService, AngularFireAuth, AngularFirestore
]
});
});
it('login correct', inject([AuthService], (service: AuthService) => {
spyOn(service,'emailPasswordLoginAsPromise').and.returnValue(Promise.resolve());
let login = {
email: 'email#gmail.com',
password: 'correctpassword'
}
expect(service.emailPasswordLoginAsPromise(login)).toEqual(jasmine.any(Promise));
}));
it('login incorret', inject([AuthService], (service: AuthService) => {
spyOn(service,'emailPasswordLoginAsPromise').and.returnValue(Promise.reject());
let login = {
email: 'email#gmail.com',
password: 'wrongpassword'
}
expect(service.emailPasswordLoginAsPromise(login)).toEqual(jasmine.any(Promise));
}));
});

How to test that I have loaded my JSON - Angular2+

I have an Angular method that simply loads the contents of a locally stored JSON file featuring an array, however I cannot seem to test it.
test.ts (shortened for conciseness)
describe('MyComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [MyComponent],
imports: [HttpClientTestingModule],
});
fixture = TestBed.createComponent(MyComponent);
component = fixture.componentInstance;
component.ngOnInit();
it('should load status data from local json', () => {
const data = require('../../../assets/myData.json');
component.getThings();
expect(component.data).toEqual(data);
});
}
MyComponent.ts
data: string[];
constructor(private httpClient: HttpClient) {}
ngOnInit() {
this.getThings().subscribe(data =
this.data = data;
}
}
getData(): Observable<any> {
const data = '../../../assets/data.json';
return this.httpClient.get(data);
}
When testing for http request you need to mock the request.
Check out more about HttpClientTestingModule below:
https://angular.io/api/common/http/testing/HttpClientTestingModule
https://medium.com/spektrakel-blog/angular-testing-snippets-httpclient-d1dc2f035eb8
The below code is working,updated the component code as well a bit:
Component:
import { Component, OnInit } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { Observable } from 'rxjs';
#Component({
selector: 'app-load-local-file',
templateUrl: './load-local-file.component.html',
styleUrls: ['./load-local-file.component.css']
})
export class LoadLocalFileComponent implements OnInit {
data: string[];
constructor(private httpClient: HttpClient) { }
ngOnInit() {
this.getData().subscribe(data => {
this.data = data;
});
}
getData(): Observable<any> {
const data = './data.json';
return this.httpClient.get(data);
}
}
Spec:
import { TestBed, async, fakeAsync, tick } from '#angular/core/testing';
import { HttpClientTestingModule, HttpTestingController } from '#angular/common/http/testing'
import { LoadLocalFileComponent } from 'src/app/load-local-file/load-local-file.component';
describe('MyComponent', () => {
let fixture, component, httpMock: HttpTestingController;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [LoadLocalFileComponent],
imports: [HttpClientTestingModule]
});
fixture = TestBed.createComponent(LoadLocalFileComponent);
component = fixture.componentInstance;
httpMock = TestBed.get(HttpTestingController);
}));
it('should load status data from local json', fakeAsync(() => {
const data = require('./data.json');
component.ngOnInit();
tick();
const request = httpMock.expectOne('./data.json');
request.flush(data);
expect(component.data).toEqual(data);
}));
});

Can't run service in Angular2 test (code copy pasted from the docs)

I have the following error (unit testing Angular2):
Cannot configure the test module when the test module has already been
instantiated. Make sure you are not using inject before
TestBed.configureTestingModule
Here is my code (it's basically a copy paste from the angular docs) which throws the above error:
import { TestBed, async } from '#angular/core/testing';
import { By } from '#angular/platform-browser';
import { AppModule } from './app.module';
import { AppComponent } from './app.component'
import { MyServiceService } from './my-service.service'
beforeEach(() => {
// stub UserService for test purposes
let userServiceStub = {
isLoggedIn: true,
user: { name: 'Test User'}
};
TestBed.configureTestingModule({
declarations: [ AppComponent],
providers: [ {provide: MyServiceService, useValue: userServiceStub } ]
});
let fixture = TestBed.createComponent(AppComponent);
let comp = fixture.componentInstance;
// UserService from the root injector
let userService = TestBed.get(MyServiceService);
// get the "welcome" element by CSS selector (e.g., by class name)
let de = fixture.debugElement.query(By.css('.nesto'));
let el = de.nativeElement;
it('should welcome "Bubba"', () => {
userService.user.name = 'something'; // welcome message hasn't been shown yet
fixture.detectChanges();
expect(el.textContent).toContain('some');
});
});
I want to run a service but it seems that I just can't do that.
The most likely problem is that you're attempting to run testing within your beforeEach(). You need to make sure all it() methods are outside/after the beforeEach():
beforeEach(() => {
// stub UserService for test purposes
let userServiceStub = {
isLoggedIn: true,
user: { name: 'Test User'}
};
TestBed.configureTestingModule({
declarations: [ AppComponent],
providers: [ {provide: MyServiceService, useValue: userServiceStub } ]
});
let fixture = TestBed.createComponent(AppComponent);
let comp = fixture.componentInstance;
// get the "welcome" element by CSS selector (e.g., by class name)
let de = fixture.debugElement.query(By.css('.nesto'));
let el = de.nativeElement;
});
it('should welcome "Bubba"', inject([MyServiceService], (userService) => {
userService.user.name = 'something'; // welcome message hasn't been shown yet
fixture.detectChanges();
expect(el.textContent).toContain('some');
}));
This works, removed the instance in the beforeEach() and injected into the it().
All credits go to Z. Bagley
import { TestBed, async, inject } from '#angular/core/testing';
import { By } from '#angular/platform-browser';
import { AppModule } from './app.module';
import { AppComponent } from './app.component'
import { MyServiceService } from './my-service.service'
import { Inject } from '#angular/core';
beforeEach(() => {
// stub UserService for test purposes
let userServiceStub = {
isLoggedIn: true,
user: { name: 'Test User'}
};
TestBed.configureTestingModule({
declarations: [ AppComponent],
providers: [ {provide: MyServiceService, useValue: userServiceStub } ]
});
});
it('should welcome "Bubba"', inject([MyServiceService], (userService) => {
let fixture=TestBed.createComponent(AppComponent);
fixture.detectChanges();
expect(userService.user.name).toContain('se');
}));

Categories

Resources