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

Angular 15中Header组件Cookie单元测试报错求助

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 08:45:55