如何使用Jasmine/Karma为返回Promise数组的Angular方法编写单元测试(Esri模块加载场景)
解决Angular单元测试中模拟Esri Loader模块的问题
你的问题主要有两个核心点:模拟返回值的类型不匹配(误用了Observable而非Promise),以及没有正确模拟Esri模块导出的构造函数。下面一步步帮你解决:
1. 问题根源分析
你之前的测试代码用of([{},{}])返回了RxJS Observable,但esriLoader.loadModules()实际返回的是Promise。当loadEsriModules里调用.then()时,Observable没有这个方法,直接抛出错误进入catch块。另外,Esri模块导出的是可实例化的构造函数(函数类型),而非普通对象,普通对象无法满足后续代码可能的实例化操作。
2. 正确的模拟方式
第一步:创建模拟的Esri构造函数
因为实际返回的是Esri的类(比如GeoJSONLayer是一个可以new的函数),我们需要模拟这些构造函数,甚至可以添加代码中用到的属性(比如declaredClass):
// 模拟GeoJSONLayer类 class MockGeoJSONLayer { declaredClass = 'esri.layers.GeoJSONLayer'; // 如果你的代码用到了该类的方法,比如add(),可以添加模拟实现 add = jasmine.createSpy('add'); // Jasmine语法,Jest用jest.fn() } // 模拟Graphic类 class MockGraphic { declaredClass = 'esri.Graphic'; // 同样添加需要的模拟方法/属性 }
如果不想用class,直接用函数也可以:
const MockGeoJSONLayer = function() {}; MockGeoJSONLayer.prototype.declaredClass = 'esri.layers.GeoJSONLayer'; const MockGraphic = function() {}; MockGraphic.prototype.declaredClass = 'esri.Graphic';
第二步:正确模拟loadModules方法
确保返回的是Promise,而不是Observable,并且传入模拟的构造函数数组:
// 在你的测试用例中 spyOn(esriLoaderService, "loadModules").and.callFake(() => { // 返回Promise.resolve包裹的模拟构造函数数组 return Promise.resolve([MockGeoJSONLayer, MockGraphic]); }); // 或者用更简洁的returnValue写法 spyOn(esriLoaderService, "loadModules").and.returnValue( Promise.resolve([MockGeoJSONLayer, MockGraphic]) );
3. 完整测试示例
假设你用Jasmine,完整的测试代码大概是这样:
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { YourComponent } from './your.component'; import { EsriLoaderService } from './path-to-esri-loader.service'; describe('YourComponent', () => { let component: YourComponent; let fixture: ComponentFixture<YourComponent>; let esriLoaderService: jasmine.SpyObj<EsriLoaderService>; // 模拟Esri构造函数 class MockGeoJSONLayer { declaredClass = 'esri.layers.GeoJSONLayer'; } class MockGraphic { declaredClass = 'esri.Graphic'; } beforeEach(async () => { const esriLoaderSpy = jasmine.createSpyObj('EsriLoaderService', ['loadModules']); await TestBed.configureTestingModule({ declarations: [YourComponent], providers: [ { provide: EsriLoaderService, useValue: esriLoaderSpy } ] }).compileComponents(); esriLoaderService = TestBed.inject(EsriLoaderService) as jasmine.SpyObj<EsriLoaderService>; // 预先设置模拟返回值 esriLoaderService.loadModules.and.returnValue( Promise.resolve([MockGeoJSONLayer, MockGraphic]) ); }); beforeEach(() => { fixture = TestBed.createComponent(YourComponent); component = fixture.componentInstance; fixture.detectChanges(); // 触发ngOnInit }); it('should load Esri modules successfully', () => { expect(esriLoaderService.loadModules).toHaveBeenCalledWith([ "esri/layers/GeoJSONLayer", "esri/Graphic" ]); expect(component.geoJsonLayer).toBe(MockGeoJSONLayer); expect(component.graphic).toBe(MockGraphic); }); });
4. 额外提示
如果你的后续代码需要实例化这些类(比如new this.geoJsonLayer(...)),可以在模拟类中添加构造函数逻辑,或者直接验证实例化的调用。
内容的提问来源于stack exchange,提问作者Palash Agrawal
相关产品推荐
相关产品推荐

