Angular 7: Test Guard
I will show you how you can test a guard. I'd like to test the code below:
We have in this code mainly 3 cases to test:
You can run your test with
Thanks for sharing...
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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:
- The logged in user has the required role. He should be able to access the restricted area.
- 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.
- The user is NOT logged in. He should not be able to access the restricted area. The user is redirected to the login page.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
Post a Comment