如何使用Karma-Jasmine为动态JSON配置文件编写测试用例
Angular动态配置服务Karma+Jasmine测试实现方案
核心测试覆盖点
测试需要覆盖以下核心逻辑,保证动态配置流程符合预期:
- 服务实例可正常创建
- 配置加载方法执行前,
configgetter默认返回undefined loadConfiguration方法会向正确的路径发起GET请求,请求成功后正确缓存配置值,且返回值与缓存值一致loadConfiguration请求失败时会正确抛出异常,且不会污染缓存的配置值APP_INITIALIZER注册的工厂函数会正确触发配置加载逻辑,保证应用初始化前完成配置读取
具体实现代码
依赖准备
单元测试不需要发起真实HTTP请求,使用Angular官方提供的HttpClientTestingModule拦截请求做mock即可,首先调整测试文件的引入项:
import { TestBed, Injector } from '@angular/core/testing'; import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; import { ConfigurationService } from './configuration.service'; import { configLoader } from './app.module'; // 按项目实际路径调整
ConfigurationService单元测试
替换原有configuration.service.spec.ts中的测试逻辑,覆盖服务核心行为:
describe('ConfigurationService', () => { let service: ConfigurationService; let httpMock: HttpTestingController; // 测试用固定配置路径和mock配置值 const TEST_CONFIG_PATH = '../assets/demo/data/config.json'; const MOCK_VALID_CONFIG = { sampleConfigValue: 'mock config value for test' }; beforeEach(() => { TestBed.configureTestingModule({ imports: [HttpClientTestingModule] }); service = TestBed.inject(ConfigurationService); httpMock = TestBed.inject(HttpTestingController); }); // 每个用例执行完后校验没有未处理的多余请求 afterEach(() => { httpMock.verify(); }); it('should be created successfully', () => { expect(service).toBeTruthy(); }); it('should return undefined for config property before loadConfiguration is called', () => { expect(service.config).toBeUndefined(); }); it('should load config from correct path, cache value and return config when request succeeds', async () => { // 触发配置加载,先不等待Promise完成 const loadTask = service.loadConfiguration(); // 拦截发起的HTTP请求,校验请求方法和路径 const req = httpMock.expectOne(TEST_CONFIG_PATH); expect(req.request.method).toBe('GET'); // 返回mock的成功响应 req.flush(MOCK_VALID_CONFIG); // 等待加载逻辑执行完成 const returnedValue = await loadTask; // 校验返回值和缓存值一致 expect(returnedValue).toEqual(MOCK_VALID_CONFIG); expect(service.config).toEqual(MOCK_VALID_CONFIG); }); it('should reject with error and keep config as undefined when request fails', async () => { const loadTask = service.loadConfiguration(); const req = httpMock.expectOne(TEST_CONFIG_PATH); // 模拟404错误响应 req.flush('Config file not found', { status: 404, statusText: 'Not Found' }); // 校验Promise异常抛出 await expectAsync(loadTask).toBeRejected(); // 校验加载失败时配置缓存未被污染 expect(service.config).toBeUndefined(); }); });
APP_INITIALIZER工厂函数测试
单独验证初始化工厂逻辑,保证应用启动时会触发配置加载:
describe('APP_INITIALIZER Config Loader', () => { let injector: Injector; let configServiceSpy: jasmine.SpyObj<ConfigurationService>; beforeEach(() => { // 创建ConfigurationService的mock spy对象 const serviceSpy = jasmine.createSpyObj('ConfigurationService', ['loadConfiguration']); TestBed.configureTestingModule({ providers: [ { provide: ConfigurationService, useValue: serviceSpy } ] }); injector = TestBed.inject(Injector); configServiceSpy = TestBed.inject(ConfigurationService) as jasmine.SpyObj<ConfigurationService>; }); it('should generate init function that triggers ConfigurationService.loadConfiguration', () => { // mock加载方法返回成功Promise configServiceSpy.loadConfiguration.and.returnValue(Promise.resolve({ sampleConfigValue: 'mock config value for test' })); // 执行工厂得到初始化钩子函数 const initHook = configLoader(injector); initHook(); // 校验配置加载方法被正确调用 expect(configServiceSpy.loadConfiguration).toHaveBeenCalledTimes(1); }); });
测试说明
- 所有HTTP请求通过
HttpTestingController拦截,不会发起真实网络请求,测试执行速度快、稳定性高 afterEach中调用httpMock.verify()可以自动检测是否存在意外发起的未处理请求,避免漏测请求逻辑- 如果你使用的RxJS版本>=7,建议将服务中
toPromise()替换为firstValueFrom(从rxjs导入),该API在新版本中替代了废弃的toPromise,上述测试用例不需要调整即可兼容 - 如果需要做全链路集成测试,可以在
AppComponent测试中校验应用启动后ConfigurationService.config存在正确值,单元测试阶段覆盖上述逻辑即可满足要求
内容的提问来源于stack exchange,提问作者ftslaptop pc
相关产品推荐
相关产品推荐

