测试依赖HTTP请求的Angular路由守卫报错,如何解决?
问题:Angular守卫测试报错
TypeError: this.auth.isAuthenticated(...).pipe is not a function 我在测试Angular项目的认证守卫时遇到了这个错误,想请教大家怎么正确编写测试用例。以下是我的相关代码:
认证服务代码
isAuthenticated () { const token = localStorage.getItem('token'); if (token) { return this.verifyToken(token).pipe( map(data => true), catchError (error => of(false)) ); } return of(false); } verifyToken (token: string): Observable<any> { const data = {'token': token}; return this.http.post('url/verify/', data); }
守卫的canActivate方法
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { return this.auth.isAuthenticated().pipe( map(data => { if (data) { this.router.navigate(['/url/']); } return !data; }) ); }
测试代码
let guard: Guard; let service: Service; beforeEach(() => { TestBed.configureTestingModule({ imports: [CommonModule], providers: [ Service, Guard, ] }).compileComponents(); guard = TestBed.get(Guard); service = TestBed.get(Service); }); it("can't see login page when he is logged in", fakeAsync(() =>{ spyOn(service, 'isAuthenticated').and.callFake(()=>{return true;}); guard.canActivate(new ActivatedRouteSnapshot(), mockSnapshot).subscribe( x => expect(x).toBeFalsy() ); }));
错误提示
TypeError: this.auth.isAuthenticated(...).pipe is not a function
解决方案
嘿,这个问题的根源很容易找到:你在spyOn的时候让isAuthenticated直接返回了普通布尔值true,但实际上这个方法的返回类型是Observablepipe方法,普通布尔值当然没有,所以才会抛出这个错误。
你只需要把callFake的返回值改成发出true的Observable就行,用RxJS的of操作符来实现:
// 先确保导入了of操作符 import { of } from 'rxjs'; it("can't see login page when he is logged in", fakeAsync(() => { // 把return true改成return of(true) spyOn(service, 'isAuthenticated').and.callFake(() => of(true)); guard.canActivate(new ActivatedRouteSnapshot(), mockSnapshot).subscribe( x => expect(x).toBeFalsy() ); }));
另外还有几个小细节可以优化一下,让测试更健壮:
- 别忘了在测试文件顶部导入
of操作符,不然会报错 - 你的测试模块里没有导入
HttpClientTestingModule,如果后续要测试verifyToken这类依赖HTTP请求的方法,一定要加上这个模块 - 守卫里用到了
Router,最好给它提供一个mock,避免this.router.navigate在测试中抛出错误,推荐用RouterTestingModule来配置:
import { HttpClientTestingModule } from '@angular/common/http/testing'; import { RouterTestingModule } from '@angular/router/testing'; beforeEach(() => { TestBed.configureTestingModule({ imports: [CommonModule, HttpClientTestingModule, RouterTestingModule], providers: [Service, Guard] }).compileComponents(); // Angular 9+推荐用inject代替get,类型更安全 guard = TestBed.inject(Guard); service = TestBed.inject(Service); });
这样修改之后,你的测试应该就能正常运行了,因为现在isAuthenticated返回的是一个合法的Observable,pipe方法就能正常调用啦。
内容的提问来源于stack exchange,提问作者peryztor
相关产品推荐
相关产品推荐

