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

如何使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 12:32:47