You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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错误。

解决步骤

  1. 修改Router模拟对象,让navigate spy返回resolved状态的Promise:
{
  provide: Router,
  useValue: {
    navigate: jasmine.createSpy('navigate').and.returnValue(Promise.resolve(true))
  }
}
  1. 模拟全局window.location.reload(),避免测试时真实刷新页面:
    在beforeEach中添加:
spyOn(window.location, 'reload');
  1. 移除重复导入,测试模块中重复导入了RouterTestingModule,保留带路由配置的那一个即可,同时建议给TranslateModule添加forRoot()初始化:
imports: [
  CommonModule,
  TranslateModule.forRoot(),
  HttpClientModule,
  RouterTestingModule.withRoutes([
    {
      path: 'home',
      component: HomeComponent
    }
  ])
]
  1. 异步测试优化,使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 04:50:25