Angular DynamicFormService服务类单元测试编写指导咨询
Angular DynamicFormService 规范单元测试实现方案
核心排坑说明
你之前用spyOn实现测试时报错,核心原因通常是两个:
- 尝试对正在测试的服务方法本身打桩,直接绕开方法内部的请求逻辑,既达不到测试目的,还会因为异步请求流程不完整抛错
- 没有正确使用
HttpClientTestingModule提供的请求拦截能力,测试结束后残留未处理的异步请求,触发环境校验报错
你已经在spec中引入HttpClientTestingModule、RouterTestingModule的操作是正确的,只需要补充HttpTestingController拦截器的注入,完全不需要修改服务本身的源码。
测试环境初始化
按照下面的结构初始化测试套件,适配服务自定义通过HttpBackend创建HttpClient的场景:
import { TestBed } from '@angular/core/testing'; import { HttpTestingController, HttpClientTestingModule } from '@angular/common/http/testing'; import { RouterTestingModule } from '@angular/router/testing'; import { DynamicFormService } from './dynamic-form.service'; describe('DynamicFormService', () => { // 待测试的服务实例 let service: DynamicFormService; // 请求拦截器,替代手写spy的操作 let httpMock: HttpTestingController; beforeEach(() => { TestBed.configureTestingModule({ imports: [ HttpClientTestingModule, RouterTestingModule ], providers: [DynamicFormService] }); // 从测试注入器中获取实例 service = TestBed.inject(DynamicFormService); httpMock = TestBed.inject(HttpTestingController); }); // 每个测试执行完成后校验所有请求都被正确处理,避免用例之间串扰 afterEach(() => { httpMock.verify(); });
各服务方法的测试用例实现
每个方法的测试需要覆盖两个核心校验点:
- 服务发出的请求类型、URL、携带的参数是否符合接口定义
- 服务对接口返回的响应数据、错误响应的处理逻辑是否正确
getDynamicFormData 方法测试
it('getDynamicFormData 应该发送携带表单ID参数的GET请求,正确返回动态表单配置', () => { // 准备模拟的接口返回数据 const mockFormConfig = { formItems: [], globalConfig: { name: '测试表单' } }; const testFormId = 'FORM_001'; // 调用服务方法,订阅触发请求发送 service.getDynamicFormData(testFormId).subscribe(response => { // 断言返回的数据和模拟值一致 expect(response).toEqual(mockFormConfig); }); // 匹配测试套件中发出的对应请求 const testRequest = httpMock.expectOne(request => { return request.method === 'GET' && request.url === '/api/dynamic-form' && request.params.get('formId') === testFormId; }); // 校验请求方法为GET expect(testRequest.request.method).toBe('GET'); // 为模拟请求喂入准备好的响应数据 testRequest.flush(mockFormConfig); });
getTaskByWorkflowId 方法测试
it('getTaskByWorkflowId 应该根据工作流ID发起GET请求,返回关联的任务列表', () => { const mockTaskList = [ { taskId: 'T001', status: 'pending' }, { taskId: 'T002', status: 'completed' } ]; const testWorkflowId = 'WF_2024001'; service.getTaskByWorkflowId(testWorkflowId).subscribe(response => { expect(response).toEqual(mockTaskList); }); const testRequest = httpMock.expectOne(`/api/task?workflowId=${testWorkflowId}`); expect(testRequest.request.method).toBe('GET'); testRequest.flush(mockTaskList); });
getAccontType 方法测试
it('getAccontType 应该发起GET请求,返回账户类型枚举列表', () => { const mockAccountTypeList = [ { value: 1, label: '企业账户' }, { value: 2, label: '个人账户' } ]; service.getAccontType().subscribe(response => { expect(response).toEqual(mockAccountTypeList); }); const testRequest = httpMock.expectOne('/api/account-type'); expect(testRequest.request.method).toBe('GET'); testRequest.flush(mockAccountTypeList); });
补充异常场景测试
it('当动态表单接口返回服务端错误时,方法应该正确抛出错误', () => { const testFormId = 'INVALID_FORM_ID'; const mockErrorResponse = { status: 500, statusText: 'Internal Server Error' }; service.getDynamicFormData(testFormId).subscribe({ next: () => fail('接口异常时不应该触发成功回调'), error: (err) => { expect(err.status).toBe(500); } }); const testRequest = httpMock.expectOne('/api/dynamic-form'); testRequest.flush(mockErrorResponse, mockErrorResponse); });
内容的提问来源于stack exchange,提问作者Sparsh Verma
相关产品推荐
相关产品推荐

