Angular 5:如何测试依赖window/webkitTemporaryStorage的StorageService?
解决Angular StorageService单元测试中Mock window/webkitTemporaryStorage的问题
我完全理解你遇到的困扰——直接操作window或navigator.webkitTemporaryStorage时会碰到只读属性的限制,而且spyOn的方式也没达到预期效果。下面给你两种可行的测试方案,附带详细的代码示例:
方案一:通过Mock全局navigator对象实现测试
这种方式贴合Angular测试的规范,我们可以在测试初始化阶段覆盖全局的navigator属性:
import { TestBed } from '@angular/core/testing'; import { StorageService } from './storage.service'; describe('StorageService', () => { let service: StorageService; let mockQueryUsageAndQuota: jasmine.Spy; beforeEach(() => { // 创建Mock的webkitTemporaryStorage对象 const mockWebkitStorage = { queryUsageAndQuota: jasmine.createSpy('queryUsageAndQuota').and.callFake((callback) => { // 默认传入80%使用率的测试数据 callback(800, 1000); }) }; // 覆盖全局navigator的webkitTemporaryStorage属性 (window.navigator as any).webkitTemporaryStorage = mockWebkitStorage; TestBed.configureTestingModule({ providers: [StorageService] }); service = TestBed.inject(StorageService); mockQueryUsageAndQuota = (window.navigator as any).webkitTemporaryStorage.queryUsageAndQuota; }); it('should return true when used storage is within 80% of granted quota', (done) => { service.hasAvailableStorage().subscribe(result => { expect(result).toBe(true); expect(mockQueryUsageAndQuota).toHaveBeenCalled(); done(); // 异步Observable测试必须调用done通知完成 }); }); it('should return false when used storage exceeds 80% of granted quota', (done) => { // 重新Mock回调逻辑,传入超过阈值的数据 mockQueryUsageAndQuota.and.callFake((callback) => { callback(900, 1000); }); service.hasAvailableStorage().subscribe(result => { expect(result).toBe(false); expect(mockQueryUsageAndQuota).toHaveBeenCalled(); done(); }); }); });
方案二:使用spyOnProperty处理只读属性
如果直接赋值webkitTemporaryStorage时报错(因为它是只读属性),可以用spyOnProperty来模拟这个只读属性:
import { TestBed } from '@angular/core/testing'; import { StorageService } from './storage.service'; describe('StorageService', () => { let service: StorageService; let mockWebkitStorage: { queryUsageAndQuota: jasmine.Spy }; beforeEach(() => { mockWebkitStorage = { queryUsageAndQuota: jasmine.createSpy('queryUsageAndQuota') }; // 用spyOnProperty模拟只读的webkitTemporaryStorage属性 spyOnProperty(window.navigator, 'webkitTemporaryStorage', 'get').and.returnValue(mockWebkitStorage); TestBed.configureTestingModule({ providers: [StorageService] }); service = TestBed.inject(StorageService); }); it('should emit true when usage is under 80%', (done) => { mockWebkitStorage.queryUsageAndQuota.and.callFake((callback) => { callback(400, 500); // 80%使用率场景 }); service.hasAvailableStorage().subscribe(res => { expect(res).toBe(true); expect(mockWebkitStorage.queryUsageAndQuota).toHaveBeenCalled(); done(); }); }); });
核心注意事项
- 因为
hasAvailableStorage返回的是异步Observable,测试时必须使用done回调来通知测试框架异步操作完成,否则测试会提前结束导致断言失败。 - 每个
beforeEach会重新初始化测试环境,所以不需要额外恢复原生的window或navigator对象。 - 如果你的项目使用RxJS 6+版本,注意
Observable.create的写法已经更新,但测试逻辑可以保持一致。
内容的提问来源于stack exchange,提问作者Robbert Raats
相关产品推荐
相关产品推荐

