angular - Cum să testezi un Functional Router Guard cu Parametru în cadrul runInInjectionContext
Implementez un gardian funcțional pentru rutare în Angular 15.2.9, care verifică dacă un utilizator este autentificat. Când acest lucru nu este cazul, gardianul ar trebui să returneze fie false, fie un UrlTree (adică redirecționare către pagina de autentificare), în funcție de parametrul redirectToLogin: boolean.
Folosesc o funcție de fabrică pentru protecția rutelor funcționale similară cu cea din acest articol:
export const isLoggedIn = (redirectToLogin: boolean): CanActivateFn => {
return (next: ActivatedRouteSnapshot, state: RouterStateSnapshot) => {
const isLoggedIn = !!inject(CookieService).check('some_cookie');
if (isLoggedIn) return true;
if (!isLoggedIn && !redirectToLogin) return false;
const redirectUrl = inject(Router).createUrlTree(['user', 'sign_in'], {
queryParams: { next: encodeURIComponent(`/${next.url.join('/')}`) },
queryParamsHandling: 'merge',
});
return redirectUrl;
};
};
Funcționează așa cum era de așteptat, dar nu pot să scriu specs pentru el:
let cookieServiceSpy;
let activatedRouteSnapshot;
beforeEach(() => {
cookieServiceSpy = jasmine.createSpyObj<CookieService>(['check']);
activatedRouteSnapshot = { url: [], data: {} };
TestBed.configureTestingModule({
providers: [
isLoggedIn,
{ provide: CookieService, useValue: cookieServiceSpy },
{
provide: ActivatedRouteSnapshot,
useValue: activatedRouteSnapshot,
},
],
});
});
it('should return true when secure_user_id cookie is present and redirectToLogin is false', () => {
// given
cookieServiceSpy.check.and.returnValue(true);
activatedRouteSnapshot.data.redirectToLogin = false;
// when
const result = TestBed.runInInjectionContext(isLoggedIn); // <-- this line is the problem
// then
expect(result).toBeTrue();
});
duce la o eroare TypeScript: Argumentul de tip '(redirectToLogin: boolean) => CanActivateFn' nu poate fi atribuit parametrului de tip '() => CanActivateFn'. Semnătura țintă oferă prea puține argumente. Se aștepta 1 sau mai multe, dar s-a primit 0.
Am încercat și câteva alte opțiuni:
TestBed.runInInjectionContext(isLoggedIn as any)➔ Nu se pot rezolva toți parametrii pentru isLoggedIn: (?). în rularea testuluiTestBed.runInInjectionContext(isLoggedIn(false))➔ Argumentul de tip 'CanActivateFn' nu poate fi atribuit parametrului de tip '() => boolean | UrlTree | Observable<boolean | UrlTree> | Promise<boolean | UrlTree>'. Semnătura țintă oferă prea puțini argumente. Se așteptau 2 sau mai mulți, dar au fost primiți 0.TestBed.runInInjectionContext(isLoggedIn(false) as any)➔ Nu se pot rezolva toți parametrii pentru isLoggedIn: (?). în rularea testuluiTestBed.runInInjectionContext(isLoggedIn(false)(activatedRouteSnapshot, null))➔ Argumentul de tip 'boolean | UrlTree | Observable<boolean | UrlTree> | Promise<boolean | UrlTree>' nu poate fi atribuit parametrului de tip '() => unknown'. Tipul 'boolean' nu poate fi atribuit tipului '() => unknown'.TestBed.runInInjectionContext(isLoggedIn(false)(activatedRouteSnapshot, null) as any);➔ inject() trebuie să fie apelat dintr-un context de injectare, cum ar fi un constructor, o funcție de fabrică, un inițializator de câmp sau o funcție folosită cuEnvironmentInjector#runInContext.TestBed.runInInjectionContext(isLoggedIn(false) as any)(activatedRouteSnapshot, null);➔ Această expresie nu poate fi apelată. Tipul ' {} ' nu are semnături de apel.(TestBed.runInInjectionContext(isLoggedIn(false) as any) as any)(activatedRouteSnapshot, null)➔ Nu pot rezolva toți parametrii pentru isLoggedIn: (?).
Am încercat, de asemenea, să omit funcția de fabrică și să folosesc Datele Rutei ca parametru, așa cum este arătat aici, dar injectarea ActivatedRouteSnapshot nu este posibilă, iar utilizarea ActivatedRoute duce la Observabile în cuib... trebuie să existe o modalitate mai ușoară.
Cum pot testa protecția funcțională a routerului?
Răspuns #1
Eroarea pe care o observați se datorează a două lucruri :
- Ați furnizat un simbol fără decor,
isLoggedIn - Prin apelarea
TestBed.runInInjectionContext, ai inițializat injectorul și injectorul a încercat să convertească furnizorii în fabrici, ceea ce a eșuat deoareceisLoggedInnu este anotat și are 1 sau mai mulți parametri (aici doar 1).
Dacă chiar aveai nevoie să injectezi CanActivateFn, ai putea folosi {provide: isLoggedIn, useValue:() => { ... }, dar în cazul nostru nu injectezi funcția, o apelezi doar.
Deci nu este nevoie să furnizați acea funcție și apelul este direct:
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();
});
