如何用Jasmine单元测试验证授权成功后及链接点击的页面跳转?
Angular Jasmine路由跳转单元测试解决方案
问题说明
需要验证两个路由跳转场景:
- 点击页面中
routerLink="/signup"的链接后跳转至/signup - 调用登录接口授权成功后跳转至Lobby页面
目前第一个场景的测试未得到预期结果。
解决方案
1. 修复点击routerLink跳转的测试
你的测试代码存在两个核心问题:
- 未在测试模块中声明
LoginComponent,导致组件无法正确初始化 - 未处理路由跳转的异步操作,需要在
fakeAsync中调用tick()触发路由完成跳转
修复后的测试代码:
import { fakeAsync, tick } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { RouterTestingModule } from '@angular/router/testing'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { TestBed, ComponentFixture } from '@angular/core/testing'; import { LoginComponent } from './login.component'; import { LobbyComponent } from '../lobby/lobby.component'; import { SignupComponent } from '../signup/signup.component'; import { routes } from '../../app-routing.module'; import { FormBuilder, MatSnackBar, Overlay } from '@angular/material'; describe('Router redirects to proper pages', () => { let fixture: ComponentFixture<LoginComponent>; let redirectLink: HTMLElement; beforeEach(() => { TestBed.configureTestingModule({ imports: [ RouterTestingModule.withRoutes(routes), HttpClientTestingModule, ], declarations: [LoginComponent, LobbyComponent, SignupComponent], providers: [FormBuilder, MatSnackBar, Overlay], }); fixture = TestBed.createComponent(LoginComponent); fixture.detectChanges(); const compiled = fixture.debugElement; redirectLink = compiled.query(By.css('[routerLink="/signup"]')).nativeElement; }); it('should redirect to /signup when clicking the signup link', fakeAsync(() => { redirectLink.click(); tick(); fixture.detectChanges(); expect(TestBed.inject(Router).url).toEqual('/signup'); })); });
2. 实现授权成功后的跳转测试
需要模拟登录接口返回成功响应,验证AuthService是否触发路由跳转至Lobby:
import { fakeAsync, tick } from '@angular/core/testing'; import { TestBed, ComponentFixture } from '@angular/core/testing'; import { LoginComponent } from './login.component'; import { AuthService } from '../../auth.service'; import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; import { environment } from '../../environments/environment'; import { BackendRoutes } from '../../constants/backend-routes'; import { Paths } from '../../constants/paths'; import { Router } from '@angular/router'; import { FormBuilder } from '@angular/forms'; import { PushNotificationService } from 'src/app/push-notification.service'; describe('LoginComponent - Authorization Success Redirect', () => { let fixture: ComponentFixture<LoginComponent>; let authService: AuthService; let httpMock: HttpTestingController; let router: Router; let component: LoginComponent; beforeEach(() => { TestBed.configureTestingModule({ imports: [HttpClientTestingModule], declarations: [LoginComponent], providers: [ AuthService, FormBuilder, { provide: PushNotificationService, useValue: { createNotification: jasmine.createSpy() } } ] }); fixture = TestBed.createComponent(LoginComponent); component = fixture.componentInstance; authService = TestBed.inject(AuthService); httpMock = TestBed.inject(HttpTestingController); router = TestBed.inject(Router); spyOn(router, 'navigate'); fixture.detectChanges(); }); it('should redirect to Lobby after successful authorization', fakeAsync(() => { // 设置表单为有效状态 component.form.patchValue({ username: 'testuser', password: 'testpass123' }); // 调用登录方法 component.userLoginClick(); // 拦截并返回成功的登录响应 const req = httpMock.expectOne(`${environment.domain}${BackendRoutes.Login}`); expect(req.request.method).toBe('POST'); req.flush({ success: true, token: 'fake-token', user: {} }); tick(); // 验证是否调用了路由跳转 expect(router.navigate).toHaveBeenCalledWith([`/${Paths.Lobby}`]); httpMock.verify(); })); });
关键注意事项
- 测试路由跳转时,必须在
fakeAsync中使用tick()处理异步操作 - 组件测试时,确保测试模块中声明了所有需要的组件和服务
- 对于依赖外部服务的逻辑,优先使用mock或spy来隔离依赖,避免真实请求影响测试结果
内容的提问来源于stack exchange,提问作者Vova Aleshchenko
相关产品推荐
相关产品推荐

