Angular Guard CanLoad 单元测试
Angular Guard CanLoad Unit Testing
我是 Angular 7 的新手,来自 AngularJS,我编写了一个实现 CanLoad 的守卫,它可以阻止没有正确声明加载模块的用户。它检查用户是否已登录以及用户是否有路由预期的声明。
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { LoadGuard } from './core/authentication/guards/load.guard';
import { MainMenuComponent } from './core/navigation/main-menu/main-menu.component';
import { PageNotFoundComponent } from './core/navigation/page-not-found/page-not-found.component';
import { UnauthorisedComponent } from './core/navigation/unauthorised/unauthorised.component';
const routes: Routes = [
{ path:'', component: MainMenuComponent, outlet: 'menu'},
{ path: 'authentication', loadChildren: './core/authentication/authentication.module#AuthenticationModule' },
{ path: 'home', loadChildren: './areas/home/home.module#HomeModule', canLoad: [LoadGuard], data: {expectedClaim: 'home'} },
{ path:"unauthorised", component: UnauthorisedComponent},
{ path:'**', component: PageNotFoundComponent }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
守卫工作正常,但我在为其编写单元测试时遇到问题。
import { Injectable } from '@angular/core';
import { CanLoad, Route, Router } from '@angular/router';
import { Observable } from 'rxjs';
import { AuthenticationService } from 'src/app/Services/Authentication/authentication.service';
@Injectable({
providedIn: 'root'
})
export class LoadGuard implements CanLoad {
constructor(private authService: AuthenticationService, private router: Router){}
canLoad(route: Route): Observable<boolean> | Promise<boolean> | boolean {
if (!route || !route.path) return false;
let isValid: boolean = this.checkLoggedIn(route.path);
if (isValid) {
if (route.data && route.data.expectedClaim) {
let expectedClaim = route.data.expectedClaim;
isValid = this.checkClaim(expectedClaim);
}
}
return isValid;
}
checkLoggedIn(url: string): boolean {
if (this.authService.checkLoggedIn()) {
return true;
}
this.authService.redirectUrl = url;
console.log('this.authService.redirectUrl (after)= ' + this.authService.redirectUrl);
this.router.navigate(['/authentication/login']);
return false;
}
checkClaim(claim: string) {
let hasClaim: boolean = false;
if (this.authService.currentUser) {
hasClaim = this.authService.currentUser.claims.indexOf(claim) > -1;
}
return hasClaim;
}
}
我下面的单元测试不起作用:
import { HttpClientModule } from '@angular/common/http';
import { fakeAsync, TestBed } from '@angular/core/testing';
import { ActivatedRouteSnapshot, Router, RouterStateSnapshot, ActivatedRoute, Route } from '@angular/router';
import { LoadGuard } from './load.guard';
class MockActivatedRouteSnapshot {
private _data: any;
get data(){
return this._data;
}
}
let mockRouterStateSnapshot : RouterStateSnapshot;
describe('LoadGuard', () => {
let loadGuard: LoadGuard;
let route: ActivatedRouteSnapshot;
let authService;
let mockRouter: any;
beforeEach(() => {
mockRouter = jasmine.createSpyObj('Router', ['navigate']);
TestBed.configureTestingModule({
imports: [
HttpClientModule,
],
providers: [
LoadGuard,
{ provide: ActivatedRouteSnapshot, useClass: MockActivatedRouteSnapshot},
{ provide: Router, useValue: mockRouter},
]
});
});
it('should be created', () => {
authService = { checkLoggedIn: () => true };
loadGuard = new LoadGuard(authService, mockRouter);
expect(loadGuard).toBeTruthy();
});
describe('check expected claims', ()=>{
it('should not be able to load an valid route needing claim when logged in without claim', fakeAsync(() => {
authService = { checkLoggedIn: () => true };
loadGuard = new LoadGuard(authService, mockRouter);
let route = new Route();
spyOnProperty(route,'data','get').and.returnValue({expectedClaim: 'policy'});
mockRouterStateSnapshot = jasmine.createSpyObj<RouterStateSnapshot>('RouterStateSnapshot', ['toString']);
mockRouterStateSnapshot.url = "test";
expect(loadGuard.canLoad(route)).toBeFalsy();
}));
});
它不允许我新建一条路线。我可能只是做错了测试。有人可以帮忙吗?
测试 canLoad()
与canActivate()
不同,canLoad()
只需要一个路由参数。 IE。 canLoad(route: Route):boolean
对比 canActivate( route: ActivatedRouteSnapshot, state: RouterStateSnapshot )
。 Route
只是我们用来定义和导出路由的接口,它应该已经存在于 TestBed 模块上下文中。因此,您根本不需要模拟它或创建它的新实例。
在您的 beforeEach(async()) 茉莉花函数中,导入带有路由的 RouterTestingModule。
TestBed.configureTestingModule({
imports: [HttpClientModule, ... ..., RouterTestingModule.withRoutes(routes)],
...
providers: [AuthenticationService, LoadGuard ] //only need to provide it here!
})
其中 routes
是您用 loadChildren
和 canLoad
定义的 export const routes: Routes = [{}]
。
注意这一点,自动导入 RouterTestingModule 提供(即注入)这些服务:
- 位置
- 位置策略
- NgModuleFactoryLoader
- 预加载策略
- 路由器
正如在此 API 文档中所见 link:https://angular.io/api/router/testing/RouterTestingModule#providers
因此,您可以简单地引用这些注入的服务而无需像您那样模拟它们。
在您的 describe() 茉莉花函数中,声明这些:
describe('AppComponent', () => {
... //all your other declarations like componentFixture, etc
loadGuard: LoadGuard;
authService: AuthenticationService;
router: Router;
location: Location;
loader: NgModuleFactoryLoader;
...
});
在你的 beforeEach() 茉莉花函数中:
location = TestBed.get(Location); //these already exist in TestBed context because of RouterTestingModule
router = TestBed.get(Router);
... //other declarations
loadGuard = TestBed.get(LoadGuard);
authService = TestBed.get(AuthenticationService);
现在,这个单元测试用例的目标是测试路由以及是否实际加载了相应的模块。
因此,您还需要在 beforeEach() 茉莉花函数中对延迟加载的模块进行存根:
loader = TestBed.get(NgModuleFactoryLoader); //already exists in TestBed context because of RouterTestingModule
loader.stubbedModules = {
'./areas/home/home.module#HomeModule': HomeModule,
... //other lazily loaded modules
}
fixture.detectChanges();
由于您已经如上所述在 configureTestingModule()
中导入了路由,因此您无需重新设置路由器配置,因为 API 规范要求您这样做(https://angular.io/api/router/testing/SpyNgModuleFactoryLoader#description ).
完成所有设置后,您就可以测试 canLoad() 保护程序了。
it('if X claims is false, user shouldn't be able to load module', fakeAsync(() => {
spyOn(authService, 'checkClaims').and.returnValue(false);
fixture.detectChanges();
router.navigateByUrl('XRoute');
tick();
expect(location.path()).toBe('/"unauthorised'); //or /404 depending on your req
}))
您不需要模拟任何其他东西或创建间谍对象等等。
尽管这个答案晚了几个月,而且很可能您现在甚至不需要它。我希望通过发布它,它将帮助其他 Angular 开发人员,因为这是 Whosebug 中唯一专门询问我也遇到过的单元测试 canLoad() 的问题。
我是 Angular 7 的新手,来自 AngularJS,我编写了一个实现 CanLoad 的守卫,它可以阻止没有正确声明加载模块的用户。它检查用户是否已登录以及用户是否有路由预期的声明。
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { LoadGuard } from './core/authentication/guards/load.guard';
import { MainMenuComponent } from './core/navigation/main-menu/main-menu.component';
import { PageNotFoundComponent } from './core/navigation/page-not-found/page-not-found.component';
import { UnauthorisedComponent } from './core/navigation/unauthorised/unauthorised.component';
const routes: Routes = [
{ path:'', component: MainMenuComponent, outlet: 'menu'},
{ path: 'authentication', loadChildren: './core/authentication/authentication.module#AuthenticationModule' },
{ path: 'home', loadChildren: './areas/home/home.module#HomeModule', canLoad: [LoadGuard], data: {expectedClaim: 'home'} },
{ path:"unauthorised", component: UnauthorisedComponent},
{ path:'**', component: PageNotFoundComponent }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
守卫工作正常,但我在为其编写单元测试时遇到问题。
import { Injectable } from '@angular/core';
import { CanLoad, Route, Router } from '@angular/router';
import { Observable } from 'rxjs';
import { AuthenticationService } from 'src/app/Services/Authentication/authentication.service';
@Injectable({
providedIn: 'root'
})
export class LoadGuard implements CanLoad {
constructor(private authService: AuthenticationService, private router: Router){}
canLoad(route: Route): Observable<boolean> | Promise<boolean> | boolean {
if (!route || !route.path) return false;
let isValid: boolean = this.checkLoggedIn(route.path);
if (isValid) {
if (route.data && route.data.expectedClaim) {
let expectedClaim = route.data.expectedClaim;
isValid = this.checkClaim(expectedClaim);
}
}
return isValid;
}
checkLoggedIn(url: string): boolean {
if (this.authService.checkLoggedIn()) {
return true;
}
this.authService.redirectUrl = url;
console.log('this.authService.redirectUrl (after)= ' + this.authService.redirectUrl);
this.router.navigate(['/authentication/login']);
return false;
}
checkClaim(claim: string) {
let hasClaim: boolean = false;
if (this.authService.currentUser) {
hasClaim = this.authService.currentUser.claims.indexOf(claim) > -1;
}
return hasClaim;
}
}
我下面的单元测试不起作用:
import { HttpClientModule } from '@angular/common/http';
import { fakeAsync, TestBed } from '@angular/core/testing';
import { ActivatedRouteSnapshot, Router, RouterStateSnapshot, ActivatedRoute, Route } from '@angular/router';
import { LoadGuard } from './load.guard';
class MockActivatedRouteSnapshot {
private _data: any;
get data(){
return this._data;
}
}
let mockRouterStateSnapshot : RouterStateSnapshot;
describe('LoadGuard', () => {
let loadGuard: LoadGuard;
let route: ActivatedRouteSnapshot;
let authService;
let mockRouter: any;
beforeEach(() => {
mockRouter = jasmine.createSpyObj('Router', ['navigate']);
TestBed.configureTestingModule({
imports: [
HttpClientModule,
],
providers: [
LoadGuard,
{ provide: ActivatedRouteSnapshot, useClass: MockActivatedRouteSnapshot},
{ provide: Router, useValue: mockRouter},
]
});
});
it('should be created', () => {
authService = { checkLoggedIn: () => true };
loadGuard = new LoadGuard(authService, mockRouter);
expect(loadGuard).toBeTruthy();
});
describe('check expected claims', ()=>{
it('should not be able to load an valid route needing claim when logged in without claim', fakeAsync(() => {
authService = { checkLoggedIn: () => true };
loadGuard = new LoadGuard(authService, mockRouter);
let route = new Route();
spyOnProperty(route,'data','get').and.returnValue({expectedClaim: 'policy'});
mockRouterStateSnapshot = jasmine.createSpyObj<RouterStateSnapshot>('RouterStateSnapshot', ['toString']);
mockRouterStateSnapshot.url = "test";
expect(loadGuard.canLoad(route)).toBeFalsy();
}));
});
它不允许我新建一条路线。我可能只是做错了测试。有人可以帮忙吗?
测试 canLoad()
与canActivate()
不同,canLoad()
只需要一个路由参数。 IE。 canLoad(route: Route):boolean
对比 canActivate( route: ActivatedRouteSnapshot, state: RouterStateSnapshot )
。 Route
只是我们用来定义和导出路由的接口,它应该已经存在于 TestBed 模块上下文中。因此,您根本不需要模拟它或创建它的新实例。
在您的 beforeEach(async()) 茉莉花函数中,导入带有路由的 RouterTestingModule。
TestBed.configureTestingModule({
imports: [HttpClientModule, ... ..., RouterTestingModule.withRoutes(routes)],
...
providers: [AuthenticationService, LoadGuard ] //only need to provide it here!
})
其中 routes
是您用 loadChildren
和 canLoad
定义的 export const routes: Routes = [{}]
。
注意这一点,自动导入 RouterTestingModule 提供(即注入)这些服务:
- 位置
- 位置策略
- NgModuleFactoryLoader
- 预加载策略
- 路由器
正如在此 API 文档中所见 link:https://angular.io/api/router/testing/RouterTestingModule#providers
因此,您可以简单地引用这些注入的服务而无需像您那样模拟它们。
在您的 describe() 茉莉花函数中,声明这些:
describe('AppComponent', () => {
... //all your other declarations like componentFixture, etc
loadGuard: LoadGuard;
authService: AuthenticationService;
router: Router;
location: Location;
loader: NgModuleFactoryLoader;
...
});
在你的 beforeEach() 茉莉花函数中:
location = TestBed.get(Location); //these already exist in TestBed context because of RouterTestingModule
router = TestBed.get(Router);
... //other declarations
loadGuard = TestBed.get(LoadGuard);
authService = TestBed.get(AuthenticationService);
现在,这个单元测试用例的目标是测试路由以及是否实际加载了相应的模块。
因此,您还需要在 beforeEach() 茉莉花函数中对延迟加载的模块进行存根:
loader = TestBed.get(NgModuleFactoryLoader); //already exists in TestBed context because of RouterTestingModule
loader.stubbedModules = {
'./areas/home/home.module#HomeModule': HomeModule,
... //other lazily loaded modules
}
fixture.detectChanges();
由于您已经如上所述在 configureTestingModule()
中导入了路由,因此您无需重新设置路由器配置,因为 API 规范要求您这样做(https://angular.io/api/router/testing/SpyNgModuleFactoryLoader#description ).
完成所有设置后,您就可以测试 canLoad() 保护程序了。
it('if X claims is false, user shouldn't be able to load module', fakeAsync(() => {
spyOn(authService, 'checkClaims').and.returnValue(false);
fixture.detectChanges();
router.navigateByUrl('XRoute');
tick();
expect(location.path()).toBe('/"unauthorised'); //or /404 depending on your req
}))
您不需要模拟任何其他东西或创建间谍对象等等。
尽管这个答案晚了几个月,而且很可能您现在甚至不需要它。我希望通过发布它,它将帮助其他 Angular 开发人员,因为这是 Whosebug 中唯一专门询问我也遇到过的单元测试 canLoad() 的问题。