我已經嘗試了幾次,但似乎我無法為 Angular 12 中非常基本的 Guard 創建單元測驗,它具有
- 可以激活
- 可以激活孩子
作為其主要方法。請找到以下代碼:
@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);
}
}
的authService內部呼叫canActivate方法應回傳通過使用一個BehaviourSubject物件而獲得可觀察到的asObservable()呼叫。
我已經嘗試了所有可能的測驗,但似乎沒有對這兩種方法進行比較(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));
});
});
});
});
});
當我運行測驗檔案時,我得到如下資訊:
Expected object to have properties _subscribe: Function Expected object not to have properties source: Observable({ _isScalar: false, source: BehaviorSubject({ _isScalar: false, observers: [ ], closed: false, isStopped: false, hasError: false, thrownE rror: null, _value: false }) }) operator: MapOperator({ project: Function, thisArg: undefined }) Error: Expected object to have properties _subscribe: Function ...
Apparently, Karma expects a ScalarObservable of some sort, plus the navigation towards ['/login'] is not detected.
Would you mind giving me some advice on how to perform this test?
Thank you in advance.
uj5u.com熱心網友回復:
以下是我將如何配置 TestBed 模塊和測驗防護:
describe('IsAuthenticatedGuard', () => {
const mockRouter = {
navigate: jasmine.createSpy('navigate'),
};
const authService = jasmine.createSpyObj('AuthService', ['getIsAuthenticated']);
let guard: IsAuthenticatedGuard;
beforeEach(
waitForAsync(() => {
TestBed.configureTestingModule({
providers: [
IsAuthenticatedGuard,
{ provide: Router, useValue: mockRouter },
{ provide: AuthService, useValue: authService },
],
}).compileComponents();
}),
);
beforeEach(() => {
guard = TestBed.inject(IsAuthenticatedGuard);
});
describe('when the user is logged in', () => {
beforeEach(() => {
authService.setIsAuthenticated.and.returnValue(of(true));
});
it('grants route access', () => {
guard.canActivate({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot).subscribe((result) => {
expect(result).toBeTrue();
});
});
it('grants child route access', () => {
guard.canActivateChild({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot).subscribe((result) => {
expect(result).toBeTrue();
});
});
});
});
uj5u.com熱心網友回復:
謝謝你,@vitaliy。
我在警衛本身和測驗檔案中調整了一些東西,并設法通過了。
這是最終的測驗檔案:
describe('IsAuthenticatedGuard', () => {
const mockRouter = {
navigate: jasmine.createSpy('navigate')
};
const authService = jasmine.createSpyObj<AuthService>('AuthService', ['getIsAuthenticated']);
let guard: IsAuthenticatedGuard;
beforeEach(
waitForAsync(() => {
void TestBed.configureTestingModule({
providers: [
IsAuthenticatedGuard,
{
provide: Router,
useValue: mockRouter
},
{
provide: AuthService,
useValue: authService
}
]
}).compileComponents();
})
);
beforeEach(() => {
guard = TestBed.inject(IsAuthenticatedGuard);
});
describe('when the user is logged in', () => {
beforeEach(() => {
authService.getIsAuthenticated.and.returnValue(of(true));
});
it('grants route access', () => {
void guard.canActivate({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot).subscribe(result => {
expect(result).toBeTrue();
});
});
it('grants child route access', () => {
guard.canActivateChild({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot).subscribe(result => {
expect(result).toBeTrue();
});
});
});
describe('when the user is logged out', () => {
beforeEach(() => {
authService.getIsAuthenticated.and.returnValue(of(false));
});
it('does not grant route access', () => {
void guard.canActivate({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot).subscribe(result => {
expect(result).toBeFalse();
expect(mockRouter.navigate).toHaveBeenCalledWith(['/login']);
});
});
it('does not grant child route access', () => {
guard.canActivateChild({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot).subscribe(result => {
expect(result).toBeFalse();
expect(mockRouter.navigate).toHaveBeenCalledWith(['/login']);
});
});
});
});
uj5u.com熱心網友回復:
您不需要 ,mockRouter因為您可以RouterTestingModule在imports陣列中添加并執行
const router: Router;
beforeEach(
waitForAsync(() => {
void TestBed.configureTestingModule({
imports: [RouterTestingModule], //ADD THIS HERE
providers: [
IsAuthenticatedGuard,
{
provide: AuthService,
useValue: authService
}
]
}).compileComponents();
})
);
beforeEach(() => {
guard = TestBed.inject(IsAuthenticatedGuard);
router = TestBed.inject(Router); //ADD THIS HERE
});
當您訂閱測驗時,您需要添加 ,waitForAsync()因為測驗應該等到每個 observable 已執行并完成。
例如
it('does not grant child route access', waitForAsync(() => {
guard.canActivateChild({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot).subscribe(result => {
expect(result).toBeFalse();
expect(mockRouter.navigate).toHaveBeenCalledWith(['/login']);
});
}));
否則可能是在呼叫 subscribe 之前測驗已經完成,而你expect什么都沒有。
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/367286.html
標籤:angular typescript karma-jasmine
上一篇:從Angular的串列中洗掉專案
下一篇:表單陣列只顯示一項
