Angular 15中Header组件Cookie单元测试报错求助
我为Angular 15项目的Header组件编写了Cookie相关单元测试,覆盖了logout函数的全部逻辑,但执行测试时会抛出两类错误:
Disconnected , because no message in 30000 ms.
或者
Some of your tests did a full page
移除该测试用例后所有测试均可正常通过,且已确认问题与版本无关。相关代码如下:
header.component.ts
import { Component, OnInit } from '@angular/core'; import { CookieService } from 'ngx-cookie-service'; @Component({ selector: 'app-header', templateUrl: './header.component.html', styleUrls: ['./header.component.scss'] }) export class HeaderComponent implements OnInit { constructor(private cookieService:CookieService) {} ngOnInit(): void {} logout(): void { localStorage.clear(); sessionStorage.clear(); let cookie:any = this.cookieService.getAll(); Object.keys(cookie)?.forEach((res:any) => { this.cookieService.delete(res, '/', '', true, 'None'); document.cookie = `${res}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`; }); window.location.href ='https://demo.demopreview.com/login/login.htm'; } }
header.component.spec.ts
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { HeaderComponent } from './header.component'; import { HttpClientModule } from '@angular/common/http'; import { CookieService } from 'ngx-cookie-service'; describe('HeaderComponent', () => { let component: HeaderComponent; let fixture: ComponentFixture<HeaderComponent>; let cookieService: CookieService; const mockWindow = { location: { href: '' } }; beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [HeaderComponent], imports:[HttpClientModule], providers: [ CookieService, { provide: 'Window', useValue: mockWindow } ] }).compileComponents(); }); beforeEach(() => { fixture = TestBed.createComponent(HeaderComponent); component = fixture.componentInstance; fixture.detectChanges(); cookieService = TestBed.inject(CookieService); }); afterEach(() => { localStorage.clear(); sessionStorage.clear(); document.cookie.split(";").forEach((c) => { document.cookie = c.replace(/^ +/, "").replace(/=.*/, "=;expires=" + new Date().toUTCString() + ";path=/"); }); }); it('should create', () => { expect(component).toBeTruthy(); }); it('should call cookieService.delete for each cookie', () => { spyOn(cookieService, 'getAll').and.returnValue({ 'testCookie1': 'value1', 'testCookie2': 'value2' }); spyOn(cookieService, 'delete'); component.logout(); expect(cookieService.delete).toHaveBeenCalledWith('testCookie1', '/', '', true, 'None'); expect(cookieService.delete).toHaveBeenCalledWith('testCookie2', '/', '', true, 'None'); }); });
问题根源
测试报错的核心原因是logout函数中直接修改了全局window.location.href,触发了实际页面跳转。Karma测试框架的上下文会因页面跳转丢失,导致测试超时断开或抛出页面跳转相关错误。另外,虽然测试代码中提供了mockWindow,但组件并未使用依赖注入的Window实例,而是直接调用全局window,导致mock失效,无法拦截跳转行为。
修复方案
1. 修改组件代码,通过依赖注入使用Window
将全局window改为依赖注入方式,方便测试时mock:
import { Component, OnInit, Inject } from '@angular/core'; import { CookieService } from 'ngx-cookie-service'; @Component({ selector: 'app-header', templateUrl: './header.component.html', styleUrls: ['./header.component.scss'] }) export class HeaderComponent implements OnInit { constructor( private cookieService: CookieService, @Inject('Window') private window: Window ) {} ngOnInit(): void {} logout(): void { localStorage.clear(); sessionStorage.clear(); let cookie: any = this.cookieService.getAll(); Object.keys(cookie)?.forEach((res: any) => { this.cookieService.delete(res, '/', '', true, 'None'); document.cookie = `${res}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`; }); this.window.location.href = 'https://demo.demopreview.com/login/login.htm'; } }
2. 优化测试代码,拦截跳转并完善断言
更新测试代码,确保mock生效,拦截页面跳转,同时补充对localStorage/sessionStorage清空、跳转地址的断言:
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { HeaderComponent } from './header.component'; import { CookieService } from 'ngx-cookie-service'; describe('HeaderComponent', () => { let component: HeaderComponent; let fixture: ComponentFixture<HeaderComponent>; let cookieService: CookieService; let mockWindow: { location: { href: string } }; beforeEach(async () => { mockWindow = { location: { href: '' } }; await TestBed.configureTestingModule({ declarations: [HeaderComponent], providers: [ CookieService, { provide: 'Window', useValue: mockWindow } ] }).compileComponents(); // Spy localStorage和sessionStorage的clear方法 spyOn(localStorage, 'clear'); spyOn(sessionStorage, 'clear'); }); beforeEach(() => { fixture = TestBed.createComponent(HeaderComponent); component = fixture.componentInstance; cookieService = TestBed.inject(CookieService); fixture.detectChanges(); }); afterEach(() => { localStorage.clear(); sessionStorage.clear(); // 清理document.cookie document.cookie.split(";").forEach((c) => { const [name] = c.trim().split('='); document.cookie = `${name}=; expires=${new Date(0).toUTCString()}; path=/;`; }); // 重置mock跳转地址 mockWindow.location.href = ''; }); it('should create', () => { expect(component).toBeTruthy(); }); it('should handle logout logic correctly', () => { // Mock cookie列表 spyOn(cookieService, 'getAll').and.returnValue({ 'testCookie1': 'value1', 'testCookie2': 'value2' }); // Spy cookie删除方法 const deleteSpy = spyOn(cookieService, 'delete'); // 执行logout component.logout(); // 断言cookie删除逻辑 expect(deleteSpy).toHaveBeenCalledTimes(2); expect(deleteSpy).toHaveBeenCalledWith('testCookie1', '/', '', true, 'None'); expect(deleteSpy).toHaveBeenCalledWith('testCookie2', '/', '', true, 'None'); // 断言存储清空 expect(localStorage.clear).toHaveBeenCalled(); expect(sessionStorage.clear).toHaveBeenCalled(); // 断言跳转地址正确 expect(mockWindow.location.href).toBe('https://demo.demopreview.com/login/login.htm'); }); });
说明
通过依赖注入Window并在测试中mock,我们可以避免实际的页面跳转,确保测试上下文稳定。同时补充完整的断言,让测试覆盖logout函数的所有逻辑分支,且不会再触发超时或页面跳转错误。
内容的提问来源于stack exchange,提问作者SGR
相关产品推荐
相关产品推荐

