Angular Jasmine测试中route.navigate结合window.location.reload报错
运行SignOutComponent Jasmine测试时的TypeError问题
我在运行SignOutComponent的Jasmine测试时出现以下错误,曾尝试相关测试方案但无效。另外,因页面无法正确跳转,在组件中使用了window.location.reload()强制刷新页面。
错误信息
An error was thrown in afterAll TypeError: this.router.navigate(...) is undefined in http://localhost:9876/_karma_webpack_/main.js (line 6552) 59104/signOut/<@http://localhost:9876/_karma_webpack_/main.js:6552:25 __tryOrUnsub@http://localhost:9876/_karma_webpack_/vendor.js:161782:16 next@http://localhost:9876/_karma_webpack_/vendor.js:161721:22 _next@http://localhost:9876/_karma_webpack_/vendor.js:161671:26 next@http://localhost:9876/_karma_webpack_/vendor.js:161648:18 subscribeToArray/<@http://localhost:9876/_karma_webpack_/vendor.js:166594:20 _trySubscribe@http://localhost:9876/_karma_webpack_/vendor.js:161093:25 subscribe@http://localhost:9876/_karma_webpack_/vendor.js:161079:22 signOut@http://localhost:9876/_karma_webpack_/main.js:6547:38 SignOutComponent@http://localhost:9876/_karma_webpack_/main.js:6544:14 SignOutComponent_Factory@ng:///SignOutComponent/ɵfac.js:5:10 hydrate@http://localhost:9876/_karma_webpack_/vendor.js:60414:35 get@http://localhost:9876/_karma_webpack_/vendor.js:60233:33 get@http://localhost:9876/_karma_webpack_/vendor.js:74309:33 inject@http://localhost:9876/_karma_webpack_/vendor.js:83930:52 inject@http://localhost:9876/_karma_webpack_/vendor.js:83826:37 63231/</<@http://localhost:9876/_karma_webpack_/main.js:6499:80 invoke@http://localhost:9876/_karma_webpack_/polyfills.js:600:26 onInvoke@http://localhost:9876/_karma_webpack_/vendor.js:167198:39 invoke@http://localhost:9876/_karma_webpack_/polyfills.js:599:52 run@http://localhost:9876/_karma_webpack_/polyfills.js:362:43 runInTestZone@http://localhost:9876/_karma_webpack_/vendor.js:167478:34 wrapTestInZone/<@http://localhost:9876/_karma_webpack_/vendor.js:167493:20 <Jasmine> invokeTask@http://localhost:9876/_karma_webpack_/polyfills.js:634:31 runTask@http://localhost:9876/_karma_webpack_/polyfills.js:406:47 drainMicroTaskQueue@http://localhost:9876/_karma_webpack_/polyfills.js:810:35
测试文件代码
import { HttpClientModule } from '@angular/common/http'; import { TestBed } from '@angular/core/testing'; import { of } from 'rxjs'; import { RouterTestingModule } from '@angular/router/testing'; import { TranslateModule } from '@ngx-translate/core'; import { SessionStorageService } from '@shared/services/session-storage/session-storage.service'; import { SignOutComponent } from './signout.component'; import { SignInService } from '@shared/services/signin.service'; import { Router } from '@angular/router'; import { HomeComponent } from '../home/home.component'; describe('SignOut Component', () => { let component: SignOutComponent; let mockRouter: jasmine.SpyObj<Router>; let mockSignInService: jasmine.SpyObj<SignInService>; beforeEach(() => { TestBed.configureTestingModule({ declarations: [SignOutComponent], imports: [ CommonModule, TranslateModule, RouterTestingModule, HttpClientModule, RouterTestingModule.withRoutes([ { path: 'home', component: HomeComponent } ]) ], providers: [ SignOutComponent, { provide: Router, useValue: { navigate: jasmine.createSpy('navigate') } }, { provide: SignInService, useValue: { signout: jasmine.createSpy('signout').and.returnValue(of({ token: 'accessToken', user: {} })) } }, { provide: SessionStorageService, useValue: { get: jasmine.createSpy('get').and.returnValue(''), set: jasmine.createSpy('set').and.returnValue('') } } ] }); component = TestBed.inject(SignOutComponent); mockRouter = TestBed.inject(Router) as jasmine.SpyObj<Router>; mockSignInService = TestBed.inject(SignInService) as jasmine.SpyObj<SignInService>; }); it('should create the component', () => { expect(component).toBeTruthy(); expect(mockSignInService.signout).toHaveBeenCalledTimes(1); expect(mockRouter.navigate).toHaveBeenCalled(); expect(mockRouter.navigate).toHaveBeenCalledWith(['/home']); }); });
组件代码
import { Router } from '@angular/router'; import { SignInService } from '@shared/services/signin.service'; import { SessionStorageService } from '@shared/services/session-storage/session-storage.service'; @Component({ selector: 'app-signout', templateUrl: './signout.component.html', styleUrls: ['./signout.component.scss'] }) export class SignOutComponent { constructor(private router: Router, private signInService: SignInService, private sessionStorageService: SessionStorageService) { this.signOut(); } signOut() { this.signInService.signout().subscribe(res => { this.sessionStorageService.set('isUserSignedIn', 'false'); this.sessionStorageService.set('token', ''); this.sessionStorageService.set('athleteId', ''); this.sessionStorageService.set('user', ''); this.router.navigate(['/home']).then(() => { window.location.reload(); }); }); } }
问题原因及解决方法
原因
错误TypeError: this.router.navigate(...) is undefined是因为Angular的router.navigate()方法返回Promise<boolean>,但测试中模拟的navigate spy没有返回任何值,导致调用.then()时出现undefined错误。
解决步骤
- 修改Router模拟对象,让
navigatespy返回resolved状态的Promise:
{ provide: Router, useValue: { navigate: jasmine.createSpy('navigate').and.returnValue(Promise.resolve(true)) } }
- 模拟全局
window.location.reload(),避免测试时真实刷新页面:
在beforeEach中添加:
spyOn(window.location, 'reload');
- 移除重复导入,测试模块中重复导入了
RouterTestingModule,保留带路由配置的那一个即可,同时建议给TranslateModule添加forRoot()初始化:
imports: [ CommonModule, TranslateModule.forRoot(), HttpClientModule, RouterTestingModule.withRoutes([ { path: 'home', component: HomeComponent } ]) ]
- 异步测试优化,使用
async/await确保异步操作完成,验证reload方法调用:
it('should create the component and perform signout actions', async () => { expect(component).toBeTruthy(); expect(mockSignInService.signout).toHaveBeenCalledTimes(1); expect(mockRouter.navigate).toHaveBeenCalledWith(['/home']); await mockRouter.navigate.calls.mostRecent().returnValue; expect(window.location.reload).toHaveBeenCalled(); });
内容的提问来源于stack exchange,提问作者user1751352
相关产品推荐
相关产品推荐

