如何为依赖 createUrlTreeFromSnapshot 的 Angular 17 CanActivateFn 防护编写 Jest 单元测试?

问题描述 投票:0回答:1

我的应用程序正在运行 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 工作,但我不知道如何设置我的测试才能使其实际工作。

angular unit-testing jestjs angular-router
1个回答
0
投票

最有可能的

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
应该具有所有属性。

© www.soinside.com 2019 - 2024. All rights reserved.