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

Angular 5中用Jest模拟window对象及修改location.href的最佳方法

我碰到过一模一样的问题!在Angular 5 + Jest的环境里测试页面重定向确实会踩这个坑——核心原因是Jest依赖的jsdom默认没有实现完整的页面导航API,直接修改window.location.href或者触发跳转就会抛出Not implemented: navigation (except hash changes)的错误。下面是我亲测有效的几种解决办法:

方案1:直接模拟window.location.href的setter(快速适配现有代码)

因为jsdom里的window.location默认是只读对象,我们可以先重定义它的属性,或者用Jest的spyOn拦截setter来绕过导航限制:

describe('YourRedirectComponent', () => {
  let component: YourRedirectComponent;
  let fixture: ComponentFixture<YourRedirectComponent>;

  beforeEach(() => {
    // 先把window.location改成可写对象,覆盖jsdom的只读限制
    Object.defineProperty(window, 'location', {
      writable: true,
      value: { href: '' }
    });

    TestBed.configureTestingModule({
      declarations: [YourRedirectComponent]
    });
    fixture = TestBed.createComponent(YourRedirectComponent);
    component = fixture.componentInstance;
  });

  it('should redirect to target URL when condition is satisfied', () => {
    // 触发你的重定向函数
    component.triggerRedirect();

    // 断言跳转地址是否正确
    expect(window.location.href).toBe('https://your-expected-url.com');
  });
});

如果想用spyOn的方式拦截setter,也可以这么写:

beforeEach(() => {
  jest.spyOn(window.location, 'href', 'set').mockImplementation(() => {});
});

it('should call redirect with correct URL', () => {
  component.triggerRedirect();
  expect(window.location.href.set).toHaveBeenCalledWith('https://your-expected-url.com');
});

方案2:封装导航服务(符合Angular最佳实践)

这个方案稍微需要重构代码,但更符合Angular的依赖注入思想,后续测试和维护会更省心。

首先创建一个导航服务,把直接操作window的逻辑封装起来:

import { Injectable } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class NavigationService {
  redirectTo(url: string): void {
    window.location.href = url;
  }
}

然后在你的组件里注入这个服务,替代直接操作window:

import { NavigationService } from './navigation.service';

@Component({...})
export class YourRedirectComponent {
  constructor(private navigationService: NavigationService) {}

  triggerRedirect(): void {
    if (this.yourCondition) {
      this.navigationService.redirectTo('https://your-target-url.com');
    }
  }
}

测试的时候,只需要mock这个服务即可,完全不用碰window:

describe('YourRedirectComponent', () => {
  let component: YourRedirectComponent;
  let fixture: ComponentFixture<YourRedirectComponent>;
  // 用Jest的Mock类型定义模拟服务
  let mockNavigationService: jest.Mocked<NavigationService>;

  beforeEach(() => {
    mockNavigationService = {
      redirectTo: jest.fn()
    } as jest.Mocked<NavigationService>;

    TestBed.configureTestingModule({
      declarations: [YourRedirectComponent],
      providers: [
        { provide: NavigationService, useValue: mockNavigationService }
      ]
    });

    fixture = TestBed.createComponent(YourRedirectComponent);
    component = fixture.componentInstance;
  });

  it('should invoke redirect service with correct URL when condition is met', () => {
    // 假设条件已满足
    component.triggerRedirect();
    
    expect(mockNavigationService.redirectTo).toHaveBeenCalledWith('https://your-target-url.com');
  });
});

方案3:修改Jest配置启用导航模拟(不推荐)

部分版本的jsdom支持通过配置开启导航模拟,但Angular 5对应的Jest版本通常比较老旧,这个方案可能不稳定,还容易导致测试用例间的状态污染。如果一定要尝试,可以在jest.config.js里添加:

module.exports = {
  // 其他已有配置...
  testEnvironmentOptions: {
    url: 'http://localhost/',
    navigation: 'enabled'
  }
};

总结

  • 如果你不想大改现有代码,方案1是最快的解决方式;
  • 追求代码架构合理性和可维护性,优先选方案2,这也是Angular官方推荐的做法。

内容的提问来源于stack exchange,提问作者David Alsh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 12:15:02