我的应用程序正在运行 Angular 17.1,并且我使用 Jest 进行单元测试。我有以下 CanActivateFn 防护
允许导航.guard.ts
import { inject } from '@angular/core';
import { CanActivateFn, createUrlTreeFromSnapshot } from '@angular/router';
import { filter, of, switchMap } from 'rxjs';
import { CoreFacade } from '@mylib/core';
export const showPensionInfoGuard: CanActivateFn = (route) => {
const coreFacade = inject(CoreFacade);
return coreFacade.allowNavigation$.pipe(
filter((status) => status !== null),
switchMap((status: boolean) => {
if (status === true) {
return of(createUrlTreeFromSnapshot(route, ['../main']));
}
return of(true);
})
);
};
此防护装置在我的应用程序中使用时按预期工作,但我在为防护装置编写适当的单元测试时遇到问题。我想确保它要么允许导航,要么返回
main
路线的 url 树。
到目前为止,我已经成功测试了“允许导航”逻辑。也就是说,守卫只返回一个值为 true 的可观察值。当 CoreFacade.allowNavigation$ 返回一个计算结果为 false 的可观察值时,就会发生这种情况。
当门面返回一个评估为 true 的可观察量时,守卫会命中 if 语句,并返回一个带有主路由 UrlTree 的可观察量。
根据我收集的信息,我似乎没有正确设置测试以允许
createUrlTreeFromSnapshot
工作。
到目前为止我的测试如下:
允许导航.guard.spec.ts
import { fakeAsync, TestBed, tick } from '@angular/core/testing';
import { ActivatedRoute, CanActivateFn, RouterStateSnapshot, UrlTree } from '@angular/router';
import { delay, noop, Observable, of } from 'rxjs';
import { cold } from 'jasmine-marbles';
import { RouterTestingModule } from '@angular/router/testing';
import { CoreFacade } from '@mylib/core';
import { allowNaviGuard } from './allowNavi.guard';
describe('allowNaviGuard', () => {
let facade: CoreFacade;
let route: ActivatedRoute;
const executeGuard: CanActivateFn = (...guardParameters) => TestBed.runInInjectionContext(() => allowNaviGuard(...guardParameters));
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
{
provide: CoreFacade,
useValue: {} as Partial<CoreFacade>,
},
{
provide: ActivatedRoute,
useValue: {
snapshot: {},
} as Partial<ActivatedRoute>,
},
],
});
facade = TestBed.inject(CoreFacade);
route = TestBed.inject(ActivatedRoute);
});
it('should allow navigation if no data', async () => {
// Set that we do not have pension info data
facade.allowNavigation$ = of(false);
expect(await executeGuard(route.snapshot, {} as RouterStateSnapshot)).toBeObservable(cold('(a|)', { a: true }));
});
it('should go to `main` if we have data, with toBeObservable', fakeAsync(() => {
// Set that we have pension info data
facade.allowNavigation$ = of(true);
expect(executeGuard(route.snapshot, {} as RouterStateSnapshot)).toBeObservable(cold('(a|)', { a: 'ffs' }));
}));
it('should go to `main` if we have data with subscription', fakeAsync(() => {
// Set that we have pension info data
facade.allowNavigation$ = of(true);
let response;
(executeGuard(route.snapshot, {} as RouterStateSnapshot) as Observable<UrlTree>).pipe(delay(100)).subscribe((val) => {
response = val;
});
tick(100);
expect(response).toBe(false);
}));
});
三个测试中的第一个有效。第二个使用 toBeObservable,在我的控制台中返回以下输出
Expected: (a|),
Received: #,
Expected:
[{"frame":0,"notification":{"kind":"N","value":"ffs"}},{"frame":0,"notification":{"kind":"C"}}]
Received:
[{"frame":0,"notification":{"kind":"E","error":{}}}],
第三个测试,使用订阅,返回:
TypeError: Cannot read properties of undefined (reading 'children')
11 | switchMap((status: boolean) => {
12 | if (status === true) {
> 13 | return of(createUrlTreeFromSnapshot(route, ['../main']));
| ^
14 | }
15 | return of(true);
16 | })
所以看来我的测试设置不允许 createUrlTreeFromSnapshot 工作,但我不知道如何设置我的测试才能使其实际工作。
最有可能的
createUrlTreeFromSnapshot
取决于真实的 ActivatedRoute
(在这种情况下不存在的属性上读取 children
)。
在这种情况下,我会
import
RouterTestingModule
所以我们有一个 ActivatedRoute
的“真实”实例。
类似这样的:
beforeEach(() => {
TestBed.configureTestingModule({
imports: [RouterTestingModule],
providers: [
{
provide: CoreFacade,
useValue: {} as Partial<CoreFacade>,
},
// Remove ActivatedRoute mock
],
});
facade = TestBed.inject(CoreFacade);
// Get a real ActivatedRoute
route = TestBed.inject(ActivatedRoute);
});
希望通过上述方法,您不会遇到
children of undefined
,因为 route: ActivatedRoute
应该具有所有属性。