Angular 7: Test Guard

I will show you how you can test a guard. I'd like to test the code below:

import { Injectable } from '@angular/core';
import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { AuthenticationService } from './../services/authentication.service';
import { User } from './../models/user.model';
/**
* Authentication and authorization guard
*/
@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate {
constructor(
private router: Router,
private authenticationService: AuthenticationService
) { }
/**
* Check if user is authenticated and authorized
* @param route
* @param state
*/
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
const currentUser = this.authenticationService.currentUserValue;
if (currentUser) {
return this.hasPermission(route, currentUser);
}
// not logged in so redirect to login page with the return url
this.router.navigate(['/login'], { queryParams: { returnUrl: state.url } });
return false;
}
/**
* Check if logged in user has permission
* @param route
* @param user
*/
private hasPermission(route: ActivatedRouteSnapshot, user: User): boolean {
if (route.data.roles && route.data.roles.indexOf(user.role) === -1) {
// if unauthorized, redirect to home page
this.router.navigate(['/']);
return false;
}
// authorized
return true;
}
}


We have in this code mainly 3 cases to test:
  1. The logged in user has the required role. He should be able to access the restricted area.
  2. The logged in user has NOT the required role. He should not be able to access the restricted area. The user is redirected to the home page.
  3. The user is NOT logged in. He should not be able to access the restricted area. The user is redirected to the login page.

import { TestBed } from '@angular/core/testing';
import { Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { AuthGuard } from './auth.guard';
import { User } from '../models/user.model';
import { Role } from '../models/role.enum';
import { AuthenticationService } from '../services/authentication.service';
class MockRouter {
navigate(path) { }
}
class MockActivatedRouteSnapshot {
private _data: any;
get data() {
return this._data;
}
}
class MockRouterStateSnapshot {
url: string = '/';
}
const adminUser: User = {
id: 1,
username: 'username',
password: 'password',
firstName: 'Remy',
lastName: 'Penchenat',
role: Role.Admin,
token: 'token'
};
class MockAuthService {
private user: User;
get currentUserValue(): User {
return this.user;
};
setAuthentication(user: User) {
this.user = user;
}
}
describe('AuthGuard', () => {
describe('canActivate', () => {
let authGuard: AuthGuard;
let authService: AuthenticationService;
let router: Router;
let route: ActivatedRouteSnapshot;
let state: RouterStateSnapshot;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [AuthGuard,
{ provide: Router, useClass: MockRouter },
{ provide: ActivatedRouteSnapshot, useClass: MockActivatedRouteSnapshot },
{ provide: AuthenticationService, useClass: MockAuthService },
{ provide: RouterStateSnapshot, useClass: MockRouterStateSnapshot }
]
});
router = TestBed.get(Router);
spyOn(router, 'navigate');
authService = TestBed.get(AuthenticationService);
//set logged in administrator by default
authService.setAuthentication(adminUser);
authGuard = TestBed.get(AuthGuard);
state = TestBed.get(RouterStateSnapshot);
});
it('Administrator can access admin route when logged in', () => {
forAdminRoute();
expect(authGuard.canActivate(route, state)).toEqual(true);
});
it('Simple user cannot access admin route when logged in', () => {
forAdminRoute();
//change role to simple user
authService.currentUserValue.role = Role.User;
expect(authGuard.canActivate(route, state)).toEqual(false);
//redirect to home page
expect(router.navigate).toHaveBeenCalledWith(['/']);
});
it('Redirect to login when user is not logged in', () => {
//no user currently logged in
authService.setAuthentication(null);
expect(authGuard.canActivate(route, state)).toEqual(false);
//redirect to login page with redirect URL
expect(router.navigate).toHaveBeenCalledWith(['/login'], Object({ queryParams: Object({ returnUrl: '/' }) }));
});
function forAdminRoute() {
route = TestBed.get(ActivatedRouteSnapshot);
spyOnProperty(route, 'data', 'get').and.returnValue({ roles: ['Admin'] });
}
});
});


You can run your test with ng test. You should see something like that:


Thanks for sharing...

Comments

Popular posts from this blog

Spring JPA : Using Specification with Projection

Chip input using Reactive Form