如何使用 runInInjectionContext 中的参数测试功能性路由器防护

fro*_*.io 5 typescript angular-routing angular angular-router-guards angular-router

我正在 Angular 15.2.9 中实现一个功能性路由器防护,它检查用户是否登录。如果不是这种情况,防护应该返回 或falsea UrlTree(即重定向到登录页面),具体取决于参数redirectToLogin: boolean

\n

我正在使用类似于本文中的功能路由防护的工厂函数:

\n
export const isLoggedIn = (redirectToLogin: boolean): CanActivateFn => {\n  return (next: ActivatedRouteSnapshot, state: RouterStateSnapshot) => {\n    const isLoggedIn = !!inject(CookieService).check(\'some_cookie\');\n\n    if (isLoggedIn) return true;\n    if (!isLoggedIn && !redirectToLogin) return false;\n\n    const redirectUrl = inject(Router).createUrlTree([\'user\', \'sign_in\'], {\n      queryParams: { next: encodeURIComponent(`/${next.url.join(\'/\')}`) },\n      queryParamsHandling: \'merge\',\n    });\n    return redirectUrl;\n  };\n};\n
Run Code Online (Sandbox Code Playgroud)\n

它的工作原理与预期一致,但我无法为其编写规范:

\n
  let cookieServiceSpy;\n  let activatedRouteSnapshot;\n\n  beforeEach(() => {\n    cookieServiceSpy = jasmine.createSpyObj<CookieService>([\'check\']);\n    activatedRouteSnapshot = { url: [], data: {} };\n    TestBed.configureTestingModule({\n      providers: [\n        isLoggedIn,\n        { provide: CookieService, useValue: cookieServiceSpy },\n        {\n          provide: ActivatedRouteSnapshot,\n          useValue: activatedRouteSnapshot,\n        },\n      ],\n    });\n  });\n\n  it(\'should return true when secure_user_id cookie is present and redirectToLogin is false\', () => {\n    // given\n    cookieServiceSpy.check.and.returnValue(true);\n    activatedRouteSnapshot.data.redirectToLogin = false;\n    // when\n    const result = TestBed.runInInjectionContext(isLoggedIn); // <-- this line is the problem\n    // then\n    expect(result).toBeTrue();\n  });\n
Run Code Online (Sandbox Code Playgroud)\n

导致打字稿错误:\'(redirectToLogin: boolean) => CanActivateFn\' 类型的参数不可分配给 \'() => CanActivateFn\' 类型的参数。\n目标签名提供的参数太少。预计 1 或更多,但得到 0。

\n

我也尝试了其他几种选择:

\n
    \n
  • TestBed.runInInjectionContext(isLoggedIn as any)\xe2\x9e\x94无法解析 isLoggedIn 的所有参数:(?)。试运行中
  • \n
  • TestBed.runInInjectionContext(isLoggedIn(false))\xe2\x9e\x94 \'CanActivateFn\' 类型的参数不可分配给 \'() => boolean | 类型的参数 网址树 | 可观察<布尔值 | 网址树> | 承诺<布尔值 | UrlTree>\'. 目标签名提供的参数太少。预计 2 或更多,但得到 0。
  • \n
  • TestBed.runInInjectionContext(isLoggedIn(false) as any)\xe2\x9e\x94无法解析 isLoggedIn 的所有参数:(?)。试运行中
  • \n
  • TestBed.runInInjectionContext(isLoggedIn(false)(activatedRouteSnapshot, null))\xe2\x9e\x94 \ 'boolean | 类型的参数 网址树 | 可观察<布尔值 | 网址树> | 承诺<布尔值 | UrlTree>\' 不可分配给类型为 \'() =>unknown\' 的参数。类型“boolean”不可分配给类型“() =>unknown”。
  • \n
  • TestBed.runInInjectionContext(isLoggedIn(false)(activatedRouteSnapshot, null) as any);\xe2\x9e\x94inject () 必须从注入上下文调用,例如构造函数、工厂函数、字段初始值设定项或与EnvironmentInjector#runInContext.
  • \n
  • TestBed.runInInjectionContext(isLoggedIn(false) as any)(activatedRouteSnapshot, null);\xe2\x9e\x94该表达式不可调用。类型“{}”没有调用签名。
  • \n
  • (TestBed.runInInjectionContext(isLoggedIn(false) as any) as any)(activatedRouteSnapshot, null)\xe2\x9e\x94无法解析 isLoggedIn 的所有参数:(?)。
  • \n
\n

我还尝试省略工厂函数并使用 Route Data 作为参数,如此处所示,注入ActivatedRouteSnapshot是不可能的,并且ActivatedRoute在嵌套的 Observables 中使用结果......必须有一种更简单的方法。

\n

如何测试路由器防护功能?

\n

Mat*_*ler 4

您观察到的错误是由于两件事造成的:

  • 您提供了一个未修饰的符号,isLoggedIn
  • 通过调用TestBed.runInInjectionContext,您初始化了注入器,并且注入器尝试将提供者转换为工厂,但由于isLoggedIn未注释且具有 1 个或多个参数(此处只有 1 个)而失败。

如果您确实需要注入,CanActivateFn您可以使用{provide: isLoggedIn, useValue: () => { ... },但在我们的例子中,您没有注入该函数,您只是调用它。

因此不需要提供该函数并且调用是直接的:

  beforeEach(() => {
    cookieServiceSpy = jasmine.createSpyObj<CookieService>(['check']);
    activatedRouteSnapshot = { url: [], data: {} };
    TestBed.configureTestingModule({
      providers: [
        { provide: CookieService, useValue: cookieServiceSpy },
      ],
    });
  });

  it('should return true when secure_user_id cookie is present and redirectToLogin is false', async () => {
    // given
    cookieServiceSpy.check.and.returnValue(true);
    activatedRouteSnapshot.data.redirectToLogin = false;
    // when
    const result = TestBed.runInInjectionContext(() => isLoggedIn(true)(activatedRouteSnapshot, undefined));  // Double call  to retrieve the result. 
    // then
    expect(result).toBeTrue();
  });
Run Code Online (Sandbox Code Playgroud)