【发布时间】:2022-01-04 09:11:25
【问题描述】:
我已经尝试了好几次,但似乎我无法为 Angular 12 中非常基本的 Guard 创建单元测试
- 可以激活
- canActivateChild
作为它的主要方法。 请找到以下代码:
@Injectable({
providedIn: 'root'
})
export class IsAuthenticatedGuard implements CanActivate, CanActivateChild {
constructor(private authService: AuthService, private router: Router) {}
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
return this.authService.getIsAuthenticated().pipe(
tap(isAuth => {
if (!isAuth) {
// Redirect to login
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.router.navigate(['/login']);
}
})
);
}
canActivateChild(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
return this.canActivate(route, state);
}
}
canActivate 方法中的authService 调用将返回一个由 BehaviourSubject 对象使用asObservable() 调用获得的 Observable。
我已经尝试了所有可能的测试,但似乎没有执行比较(toBe、toEqual 等)适用于这两种方法,执行重定向时也不会触发导航间谍。
以下是我根据网络上的一些指南创建的spec.ts 类示例:
function mockRouterState(url: string): RouterStateSnapshot {
return {
url
} as RouterStateSnapshot;
}
describe('IsAuthenticatedGuard', () => {
let guard: IsAuthenticatedGuard;
let authServiceStub: AuthService;
let routerSpy: jasmine.SpyObj<Router>;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [SharedModule, RouterTestingModule]
});
authServiceStub = new AuthService();
routerSpy = jasmine.createSpyObj<Router>('Router', ['navigate']);
guard = new IsAuthenticatedGuard(authServiceStub, routerSpy);
});
it('should be created', () => {
expect(guard).toBeTruthy();
});
const dummyRoute = {} as ActivatedRouteSnapshot;
const mockUrls = ['/', '/dtm', '/drt', '/reporting'];
describe('when the user is logged in', () => {
beforeEach(() => {
authServiceStub.setIsAuthenticated(true);
});
mockUrls.forEach(mockUrl => {
describe('and navigates to a guarded route configuration', () => {
it('grants route access', () => {
const canActivate = guard.canActivate(dummyRoute, mockRouterState(mockUrl));
expect(canActivate).toEqual(of(true));
});
it('grants child route access', () => {
const canActivateChild = guard.canActivateChild(dummyRoute, mockRouterState(mockUrl));
expect(canActivateChild).toEqual(of(true));
});
});
});
});
describe('when the user is logged out', () => {
beforeEach(() => {
authServiceStub.setIsAuthenticated(false);
});
mockUrls.forEach(mockUrl => {
describe('and navigates to a guarded route configuration', () => {
it('does not grant route access', () => {
const canActivate = guard.canActivate(dummyRoute, mockRouterState(mockUrl));
expect(canActivate).toEqual(of(false));
});
it('does not grant child route access', () => {
const canActivateChild = guard.canActivateChild(dummyRoute, mockRouterState(mockUrl));
expect(canActivateChild).toEqual(of(false));
});
it('navigates to the login page', () => {
// eslint-disable-next-line @typescript-eslint/unbound-method
expect(routerSpy.navigate).toHaveBeenCalledWith(['/login'], jasmine.any(Object));
});
});
});
});
});
当我运行测试文件时,我会得到如下信息:
预期对象具有属性 _subscribe:函数 预期对象不具有属性 来源:Observable({ _isScalar:假,来源:BehaviorSubject({_isScalar:假,观察者:[],关闭:假,isStopped:假,hasError:假,抛出E 错误:null,_value:false }) }) 运算符:MapOperator({项目:函数,thisArg:未定义}) 错误:预期对象具有属性 _subscribe:函数...
显然,Karma 需要某种 ScalarObservable,而且未检测到指向 ['/login'] 的导航。
您能否就如何执行此测试给我一些建议?
提前谢谢你。
【问题讨论】:
-
你可以提供``TestBed.configureTestingModule({ imports: [SharedModule, RouterTestingModule], providers: [...] }) 的
providers-array 中的所有服务; ``` 然后你可以把守卫从TestBed中拿出来并使用waitForAsync让angular等待所有异步操作在这个测试中完成。 -
你的常量
canActivate和canActivateChild也是 Observables,所以你不能在expect中使用它们。相反,您应该订阅它们并在那里测试结果。guard.canActivate(dummyRoute, mockRouterState(mockUrl)).subscribe((result) => expect(result).toBeTrue()) -
@FabianGosebrink 你介意在我的代码中直接给我你的答案样本吗?
-
@vitaliykotov 每当我尝试在那里输入
subscribe子句时,IntelliJ 都会抱怨它说它不能在那里调用。 -
这是因为您为
canActivate和canActivateChild设置的返回类型。如果您的AuthService.getIsAuthenticated返回Observable<boolean>,那么这些方法也应该具有这样的类型,因为它们只是返回对身份验证服务的调用
标签: angular typescript karma-jasmine