免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

Angular Material Stepper 测试指南:深入解析 MatStepperHarness 与组件测试 API

Angular Material Stepper 测试指南:深入解析 MatStepperHarness 与组件测试 API Angular Material Stepper 测试指南深入解析 MatStepperHarness 与组件测试 API【免费下载链接】componentsComponent infrastructure and Material Design components for Angular项目地址: https://gitcode.com/GitHub_Trending/co/components导读angular/material/stepper/testing是 Angular Material 官方为 Stepper分步向导组件提供的 Component Harness 测试基础设施它让开发者可以在单元测试与端到端测试中以语义化、与 DOM 结构解耦的方式定位、查询并操作步骤条。本文基于本仓库中的 API 报告文件 goldens/material/stepper/testing/index.api.md结合 src/material/stepper/testing 目录下的源码实现与 stepper-harness.spec.ts 测试用例系统讲解MatStepperHarness、MatStepHarness、MatStepperNextHarness、MatStepperPreviousHarness四个 Harness 类及全部过滤器与枚举的用法读完你将能够独立为 Stepper 编写健壮、可维护的组件测试。一、认识 Stepper Harness为什么需要它Angular Material 的所有组件都遵循 Component Harness 设计模式测试代码通过 Harness 暴露的公开 API如getLabel()、select()与组件交互而不是直接查询 CSS 类名或 DOM 节点。这样做的好处是与实现解耦即使组件内部模板重构如修改mat-step-icon-state-*类名只要 Harness 的公开 API 不变测试就无需改动跨环境复用同一套 Harness 代码可同时运行在 TestBed单元测试和 Protractor/SeleniumE2E 测试环境语义化筛选通过HarnessPredicate以 label、状态等业务维度过滤而不是手写选择器。Stepper 的 Harness 全部依赖angular/cdk/testing提供的基础设施包括ComponentHarness、ContentContainerComponentHarness、HarnessPredicate、HarnessLoader与BaseHarnessFilters。二、API 全景四个 Harness 与三类过滤器根据 index.api.md该包的公开 API 由以下内容组成本仓库入口文件 testing/index.ts 与 testing/public-api.ts 将其全部导出公开 API类型宿主选择器hostSelector职责MatStepperHarnessComponentHarness.mat-stepper-horizontal, .mat-stepper-vertical定位整个 Stepper获取方向、步骤列表、选中步骤MatStepHarnessContentContainerComponentHarnessstring.mat-step-header定位单个步骤读取标签/ARIA 属性/状态并触发选中MatStepperNextHarnessStepperButtonHarness抽象基类.mat-stepper-next定位“下一步”按钮并点击MatStepperPreviousHarnessStepperButtonHarness抽象基类.mat-stepper-previous定位“上一步”按钮并点击StepHarnessFilters接口—过滤步骤label / selected / completed / invalidStepperHarnessFilters接口—过滤 StepperorientationStepperButtonHarnessFilters接口—过滤按钮textStepperOrientation枚举—HORIZONTAL 0、VERTICAL 1注意API 报告中MatStepperNextHarness与MatStepperPreviousHarness继承自StepperButtonHarness但该基类未出现在 API 报告中说明它是包内私有抽象类定义于 stepper-button-harnesses.ts不对外暴露。从源码结构看这些 Harness 的hostSelector与真实 DOM 一一对应Stepper 根节点类名定义在 stepper.ts 的宿主绑定中而“下一步/上一步”按钮的类名则由指令 stepper-button.ts 通过host: {class: mat-stepper-next}/mat-stepper-previous注入。三、MatStepperHarness定位与筛选 Stepper 实例3.1 创建与筛选MatStepperHarness继承自ComponentHarness通过静态方法with(options?: StepperHarnessFilters)返回HarnessPredicateMatStepperHarness配合HarnessLoader使用import {MatStepperHarness} from angular/material/stepper/testing; import {TestbedHarnessEnvironment} from angular/cdk/testing/testbed; const loader TestbedHarnessEnvironment.loader(fixture); // 获取页面上所有 Stepper const all await loader.getAllHarnesses(MatStepperHarness); // 仅筛选垂直方向的 Stepper const vertical await loader.getAllHarnesses( MatStepperHarness.with({orientation: StepperOrientation.VERTICAL}) );StepperHarnessFilters唯一扩展属性是orientation?: StepperOrientation其余来自BaseHarnessFilters的selector、ancestor等通用条件。方向过滤的实现位于 stepper-harness.tswith()通过addOption(orientation, ...)注册谓词匹配时调用harness.getOrientation()与期望值比较。3.2 获取方向与步骤const stepper await loader.getHarness(MatStepperHarness); // 读取方向 const orientation: StepperOrientation await stepper.getOrientation(); // 获取所有步骤可带过滤器 const steps await stepper.getSteps(); const labeled await stepper.getSteps({label: /Two|Four/});方向判断的实现很直观stepper-harness.ts检查宿主元素是否带有mat-stepper-horizontal类有则返回StepperOrientation.HORIZONTAL否则返回VERTICAL。这要求 Stepper 根节点必须使用这两个类名之一。3.3 一键选中步骤// 选中第一个 label 为 Three 的步骤 await stepper.selectStep({label: Three});selectStep()stepper-harness.ts先按过滤器获取步骤列表若为空则抛出Error(Cannot find mat-step matching filter ...)否则选中匹配的第一个步骤。注意如果当前步骤存在校验错误Stepper 可能不允许跳转select()不一定生效源码注释明确提示了这一点。四、MatStepHarness读取步骤状态与触发选中MatStepHarness继承自ContentContainerComponentHarnessstring宿主元素为.mat-step-header。它是整个包中信息量最大的 Harness几乎覆盖了步骤的所有业务状态。4.1 标签与无障碍信息const step (await stepper.getSteps({label: Two}))[0]; const label await step.getLabel(); // 文本标签 const ariaLabel await step.getAriaLabel(); // aria-label 属性 const ariaLabelledby await step.getAriaLabelledby(); // aria-labelledby 属性getLabel()step-harness.ts读取.mat-step-text-label节点的文本同时兼容字符串标签与模板标签ng-template matStepLabel参见测试中#two-stepper的用例getAriaLabel()/getAriaLabelledby()直接读取宿主元素的对应属性。测试用例stepper-harness.spec.ts验证了mat-step aria-labelFourth step与aria-labelledbysome-label的读取结果。4.2 选中、完成与错误状态const selected await step.isSelected(); // 是否当前选中 const completed await step.isCompleted(); // 是否已完成 const hasErrors await step.hasErrors(); // 是否显示错误状态 const optional await step.isOptional(); // 是否为可选步骤这几个方法各有独特的判定逻辑isSelected()step-harness.ts读取宿主元素的aria-selected或aria-current属性任一等于true/step即视为选中——这也是为什么步骤头部模板必须正确设置这两个 ARIA 属性isCompleted()step-harness.ts通过_getIconState()读取状态图标状态为done或状态为edit且步骤未选中时视为完成hasErrors()step-harness.ts仅当图标状态为error时返回true。源码注释特别强调这不代表步骤内表单控件一定有错而是指步骤正在展示错误样式——这取决于ErrorStateMatcher的判定以及STEPPER_GLOBAL_OPTIONS注入令牌中的showErrors选项。测试中通过{provide: STEPPER_GLOBAL_OPTIONS, useValue: {showError: true}}开启该行为isOptional()step-harness.ts使用locatorForOptional(.mat-step-optional)探测“可选”标记节点是否存在存在即可选select()step-harness.ts直接点击宿主元素步骤头部。4.3 图标状态解析机制isCompleted()与hasErrors()共用私有方法_getIconState()step-harness.ts它定位.mat-step-icon从 class 属性中用正则/mat-step-icon-state-([a-z])/提取状态字符串若找不到匹配则抛出Error(Could not determine step state from ...)。这与步骤头部的渲染逻辑相呼应——MatStepHeader组件step-header.ts为不同状态提供默认图标number状态显示序号index 1edit状态显示create图标error状态显示warning图标。4.4 获取步骤内容区的 HarnessLoaderMatStepHarness作为ContentContainerComponentHarness重写了受保护的getRootHarnessLoader()step-harness.ts读取宿主元素的aria-controls属性得到内容区容器 ID再通过documentRootLocatorFactory().harnessLoaderFor(# contentId)定位。这意味着你可以在步骤内容区内部继续查找其他组件const step (await stepper.getSteps({label: Two}))[0]; const nextBtn await step.getHarness(MatStepperNextHarness); const prevBtn await step.getHarness(MatStepperPreviousHarness);这正是测试用例 stepper-harness.spec.ts 中“从步骤内取出导航按钮”的实现方式。五、MatStepperNextHarness 与 MatStepperPreviousHarness模拟步骤导航两个按钮 Harness 继承私有抽象基类StepperButtonHarness该基类只提供两个方法stepper-button-harnesses.tsasync getText(): Promisestring // 读取按钮文本 async click(): Promisevoid // 点击按钮它们各自通过with(options?: StepperButtonHarnessFilters)提供text?: string | RegExp过滤能力底层使用HarnessPredicate.stringMatches做字符串/正则匹配stepper-button-harnesses.ts。实际使用示例对应测试 stepper-harness.spec.tsconst secondStep steps[1]; await secondStep.select(); // 点击“下一步”验证选中状态前移 const nextBtn await secondStep.getHarness(MatStepperNextHarness); await nextBtn.click(); expect(await steps[2].isSelected()).toBe(true); // 点击“上一步”验证选中状态回退 const prevBtn await secondStep.getHarness(MatStepperPreviousHarness); await prevBtn.click(); expect(await steps[0].isSelected()).toBe(true);这些按钮在真实组件中对应指令button[matStepperNext]/button[matStepperPrevious]见 stepper-button.ts宿主类名.mat-stepper-next/.mat-stepper-previous与 Harness 的hostSelector完全一致。六、过滤器与枚举的完整对照6.1 StepHarnessFilters定义于 step-harness-filters.ts扩展自BaseHarnessFilters属性类型含义底层判定labelstring \| RegExp按标签过滤HarnessPredicate.stringMatches(getLabel(), label)selectedboolean按选中状态过滤(await isSelected()) selectedcompletedboolean按完成状态过滤(await isCompleted()) completedinvalidboolean按错误状态过滤(await hasErrors()) invalid四个选项在 step-harness.ts 的MatStepHarness.with()中逐一注册全部为异步谓词。6.2 StepperHarnessFilters 与 StepperButtonHarnessFiltersStepperHarnessFilters仅含orientation?: StepperOrientationstep-harness-filters.tsStepperButtonHarnessFilters仅含text?: string | RegExpstep-harness-filters.ts。6.3 StepperOrientation 枚举export enum StepperOrientation { HORIZONTAL, // 数值 0 VERTICAL, // 数值 1 }API 报告中明确标注HORIZONTAL 0、VERTICAL 1。使用时建议通过枚举成员引用而非魔法数字避免顺序变化带来的隐患。七、从源码到测试完整验证链路为验证上述 API 的实际行为仓库提供了完整的 stepper-harness.spec.ts。该测试文件同时是绝佳的使用范例其测试组件模板展示了三个典型场景#one-stepper垂直方向、纯文本标签含aria-label/aria-labelledby的步骤与导航按钮#two-stepper水平方向、模板标签ng-template matStepLabel、含optional步骤#three-stepper垂直方向、绑定stepControl的响应式表单FormGroup 必填校验用于验证错误与完成状态。覆盖的核心行为包括加载全部 Stepperexpect(steppers.length).toBe(3)、按方向筛选2 垂直 1 水平、按正则过滤步骤标签、选中步骤前后isSelected()的状态变化、文本/模板标签的读取、ARIA 属性读取、可选步骤判定、showError开启后的错误状态、setValue触发表单校验后的完成状态以及按钮点击带来的步骤前后移动。八、实践要点与最佳实践优先使用 Harness 而非 CSS 选择器所有查询都应经由MatStepperHarness及其子 Harness避免在测试中出现.mat-step-icon-state-error之类的实现细节类名善用过滤器做语义化查询getSteps({label: /Reg/, selected: true, completed: false})比手写循环筛选更易读、更稳定测试错误状态前先配置STEPPER_GLOBAL_OPTIONS如测试所示需要{provide: STEPPER_GLOBAL_OPTIONS, useValue: {showError: true}}才会渲染错误样式否则hasErrors()恒为false验证select()的局限性当步骤内容校验不通过时点击可能被 Stepper 拦截测试中应显式断言isSelected()的实际结果而不是默认选中必然成功在步骤内容区中继续下钻利用MatStepHarness的内容容器特性aria-controls关联的内容区用step.getHarness(...)查找步骤内部的表单、输入框等其他 Harness形成层级化测试结构。九、延伸阅读Harness 的基类能力HarnessPredicate、HarnessLoader、BaseHarnessFilters来源于 src/cdk/testing是理解全部 Angular Material 组件测试的基础Stepper 的业务实现见 src/material/stepper含 stepper.ts、step-header.ts、stepper-button.ts其核心状态机逻辑复用angular/cdk/stepper组件的完整功能文档可参考 src/material/stepper/stepper.md。本文所述 API 均以当前仓库 goldens/material/stepper/testing/index.api.md 为准该文件由 API Extractor 自动生成是判断公开 API 变更的权威依据。【免费下载链接】componentsComponent infrastructure and Material Design components for Angular项目地址: https://gitcode.com/GitHub_Trending/co/components创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表