ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Angular Material List 组件测试指南:`@angular/material_list_testing` 测试 Harness 全解析

Angular Material List 组件测试指南:`@angular/material_list_testing` 测试 Harness 全解析 Angular Material List 组件测试指南angular/material_list_testing测试 Harness 全解析【免费下载链接】componentsComponent infrastructure and Material Design components for Angular项目地址: https://gitcode.com/GitHub_Trending/co/components本指南以本仓库Component infrastructure and Material Design components for Angular即 Angular Material 组件库源码仓库中的 API 报告 goldens/material/list/testing/index.api.md 为核心骨架系统讲解angular/material_list_testing测试包为MatList系列组件提供的全部测试 Harness从MatListHarness、MatListItemHarness到MatSelectionListHarness、MatNavListHarness、MatActionListHarness以及MatSubheaderHarness。读完本文你将掌握每种 Harness 的公共 API、过滤器Filter参数的完整语义与取值、文本读取规则title/secondaryText/tertiaryText/fullText的区别并能在自己的 Angular 组件测试中像本仓库 src/material/list/testing/list-harness.spec.ts 那样编写类型安全、不依赖 DOM 细节的交互式测试。一、API 报告文件是什么angular/material_list_testing包的权威公共接口快照仓库根目录goldens/下存放的是各发布包的 API 黄金文件API golden files其中goldens/material/list/testing/index.api.md是 API Extractor 针对angular/material_list_testing包自动生成的报告文件头明确标注 “Do not edit this file. It is a report generated by API Extractor”因此它是对外发布的、稳定可见的公共 API 的唯一权威快照所有标记为// public的类、接口、枚举都是官方承诺对外稳定的测试 API仓库内的任何破坏性变更breaking change都必须同步更新这份 golden 文件这从流程上保证了 Harness 的向后兼容性。该包的实际实现位于 src/material/list/testing/包含list-harness.ts、list-harness-base.ts、list-item-harness-base.ts、list-harness-filters.ts、selection-list-harness.ts、nav-list-harness.ts、action-list-harness.ts等文件公共出口统一由 public-api.ts 与 index.ts 管理。所有 Harness 均构建在angular/cdk/testing的ComponentHarness、HarnessPredicate、ComponentHarnessConstructor、ContentContainerComponentHarness、BaseHarnessFilters基础之上这意味着它们可以同时运行在 TestBed 环境angular/cdk/testing/testbed与 Protractor 等真实浏览器环境中。二、Harness 家族全景6 个 List Harness 与 1 个 Subheader Harnessangular/material_list_testing依据MatList组件的四种形态提供对应的 Harness全部继承自一个共享抽象基类MatListHarnessBaseT, C, F见 list-harness-base.tsHarness 类hostSelector宿主机选择器对应组件说明MatListHarness.mat-mdc-listmat-list只读列表MatActionListHarness.mat-mdc-action-listmat-action-list可点击的操作列表MatNavListHarness.mat-mdc-nav-listmat-nav-list导航列表MatSelectionListHarness.mat-mdc-selection-listmat-selection-list带复选框/单选钮的选择列表对应的四个条目级 Harnessitem harness均继承MatListItemHarnessBase见 list-item-harness-base.tsItem Harness 类hostSelector特有能力MatListItemHarness.mat-mdc-list .mat-mdc-list-item文本读取、类型判断MatActionListItemHarness.mat-mdc-action-list .mat-mdc-list-itemclick()、focus()、blur()、isFocused()MatNavListItemHarness.mat-mdc-nav-list .mat-mdc-list-itemclick()、getHref()、isActivated()MatListOptionHarness.mat-mdc-list-optionselect()、deselect()、toggle()、isSelected()、getCheckboxPosition()、getRadioPosition()此外还有两个辅助 HarnessMatSubheaderHarnesshostSelector 为.mat-mdc-subheader直接继承ComponentHarness提供getText()读取分组标题文本。内部抽象基类MatListHarnessBase与MatListItemHarnessBase均标记为docs-private仅供以上公开类复用不作为对外 API 使用。从泛型参数可以看出设计模式每个 List Harness 通过_itemHarness属性绑定对应的 Item Harness 构造函数。例如MatListHarness extends MatListHarnessBasetypeof MatListItemHarness, MatListItemHarness, ListItemHarnessFilters并在类体中用override _itemHarness MatListItemHarness;完成绑定见 list-harness.ts。三、过滤器接口体系精确锁定目标元素的查询参数所有 Harness 的静态方法with(options?)都接收一个过滤器对象用于构造HarnessPredicate。过滤器接口之间是清晰的继承关系BaseHarnessFilters来自 angular/cdk/testing ├── ListHarnessFilters无额外字段 ├── ActionListHarnessFilters无额外字段 ├── NavListHarnessFilters无额外字段 ├── SelectionListHarnessFilters无额外字段 ├── SubheaderHarnessFilters { text?: string | RegExp } └── BaseListItemHarnessFilters ├── ListItemHarnessFilters无额外字段 ├── ActionListItemHarnessFilters无额外字段 ├── NavListItemHarnessFilters { href?; activated? } └── ListOptionHarnessFilters { selected?: boolean }3.1BaseListItemHarnessFilters条目级查询的核心参数这是 API 报告中最重要的一组字段见 list-harness-filters.tstitle?: string | RegExp按主标题文本匹配对应条目中的.mdc-list-item__primary-text元素。secondaryText?: string | RegExp | null按次行文本匹配传入null表示“筛选出没有次行文本的条目”。tertiaryText?: string | RegExp | null按第三行文本匹配同理null表示“没有第三行文本”。fullText?: string | RegExp按条目完整文本匹配排除图标与头像。text?: string | RegExp已废弃deprecated应改用fullText计划在 16.0.0 版本移除。这些字段支持string精确匹配或RegExp正则匹配其匹配逻辑集中在getListItemPredicate()函数中见 list-item-harness-base.ts内部通过HarnessPredicate.stringMatches(harness.getXxxText(), value)逐一绑定选项。注意secondaryText/tertiaryText的null值语义是特意设计的“空值筛选”与“不传该选项”有本质区别不传代表不过滤传null代表“该行文本为空”。3.2 三种 List 特有过滤器NavListItemHarnessFilters extends BaseListItemHarnessFilters增加href?: string | RegExp | null按链接地址匹配null表示筛选没有href的条目。activated?: boolean按激活状态筛选匹配mdc-list-item--activated类。ListOptionHarnessFilters extends BaseListItemHarnessFilters增加selected?: boolean按选中状态筛选。SubheaderHarnessFilters extends BaseHarnessFilters增加text?: string | RegExp按分组标题文本匹配。3.3 四个列表级过滤器ListHarnessFilters、ActionListHarnessFilters、NavListHarnessFilters、SelectionListHarnessFilters均直接继承BaseHarnessFilters即仅支持 CDK 自带的selector、ancestor等基础字段在 API 报告中体现为空接口。这意味着定位列表本身主要依靠selector而更精细的筛选全部下沉到条目级过滤器。四、枚举MatListItemSection与MatListItemType4.1MatListItemSection用于ContentContainerComponentHarness的内容区定位export enum MatListItemSection { CONTENT .mdc-list-item__content, }从源码看list-item-harness-base.tsMatListItemHarnessBase extends ContentContainerComponentHarnessMatListItemSectiongetAllHarness/locatorFor等ContentContainerComponentHarness方法可基于该枚举定位条目内容容器。4.2MatListItemType描述条目的行数变体为数字枚举export enum MatListItemType { ONE_LINE_ITEM 0, TWO_LINE_ITEM 1, THREE_LINE_ITEM 2, }getType()的实现依据宿主元素上的 MDC 类判定list-item-harness-base.ts先并行检查mdc-list-item--with-one-line与mdc-list-item--with-two-lines两个类两者都不存在则视为三行条目。该类型直接决定getSecondaryText()/getTertiaryText()的返回值策略单行条目getSecondaryText()与getTertiaryText()均返回null双行条目getSecondaryText()返回显式.mat-mdc-list-item-line第一行若无显式行则返回.mat-mdc-list-item-unscoped-content的文本自动换行场景getTertiaryText()返回null三行条目若存在两个显式行getTertiaryText()返回第二个显式行若仅一个显式行返回 unscoped 内容若没有显式行返回null表示文本只是从次行换行延续而非真正的第三行。这一“显式行 vs 非限定内容”的区分逻辑正是MatListItemHarnessBase区别于简单text()读取的关键设计也是测试中容易踩坑、值得重点理解的部分。五、List 级操作MatListHarnessBase的五个核心查询方法MatListHarnessBase见 list-harness-base.ts为所有 List Harness 提供以下方法5.1getItems(filters?)返回匹配过滤器的条目 Harness 数组底层调用locatorForAll(this._itemHarness.with(filters))。不传过滤器时返回列表内全部条目。5.2getItemsGroupedBySubheader(filters?)按MatSubheaderHarness将条目分组返回ListSectionC[]export interface ListSectionI { heading?: string; // 分组标题无标题时为 undefined items: I[]; }实现逻辑遍历“条目 子标题”的混合序列遇到MatSubheaderHarness即开启新分组最后通过parallel()并发解析所有分组的标题文本。无任何子标题的列表会被表示成单个ListSection其heading为undefined——测试断言时需注意这一点见 list-harness.spec.ts 中sections[0].heading为undefined的断言。5.3getItemsGroupedByDividers(filters?)按MatDividerHarness来自../../divider/testing即 src/material/divider/testing/将条目划分为若干子列表返回C[][]。遇到分隔线就新开一个数组无分隔线时返回单个子列表。5.4getItemsWithSubheadersAndDividers(filters?)返回条目、子标题、分隔线 Harness 的有序混合数组(C | MatSubheaderHarness | MatDividerHarness)[]调用方可用instanceof判断具体类型。过滤器对象支持item、subheader、divider三个键每个键都可以传过滤器对象或布尔值false——传false表示完全省略该类元素。该方法以函数重载的形式在 API 报告中呈现是前两个分组方法共用的底层实现getItemsGroupedBySubheader通过{divider: false}排除分隔线getItemsGroupedByDividers通过{subheader: false}排除子标题。六、条目级操作MatListItemHarnessBase的通用能力MatListItemHarnessBase见 list-item-harness-base.ts为所有条目 Harness 提供统一 API全部为Promise返回异步执行getType(): PromiseMatListItemType行数类型。getFullText(): Promisestring条目完整文本排除.mat-mdc-list-item-icon与.mat-mdc-list-item-avatar的内容源码通过host().text({exclude: .mat-mdc-list-item-icon, .mat-mdc-list-item-avatar})实现。getText(): Promisestring已废弃内部直接委托getFullText()。getTitle(): Promisestring主标题primary-text元素文本。getSecondaryText(): Promisestring | null次行文本单行条目返回null。getTertiaryText(): Promisestring | null第三行文本非三行条目返回null。isDisabled(): Promiseboolean是否包含mdc-list-item--disabled类。hasAvatar(): Promiseboolean是否存在头像元素。hasIcon(): Promiseboolean是否存在图标元素。6.1MatSelectionListHarness与MatListOptionHarness选择列表的完整交互MatSelectionListHarness见 selection-list-harness.ts在基类之外增加isDisabled(): Promiseboolean读取宿主元素的aria-disabled属性判断整个列表是否禁用。selectItems(...filters: ListOptionHarnessFilters[])对匹配任一过滤器的条目执行select()多个过滤器取并集。deselectItems(...filters: ListItemHarnessFilters[])同理执行deselect()。注意其参数类型声明为ListItemHarnessFiltersAPI 报告原样保留了这一细节但实际元素是MatListOptionHarness。MatListOptionHarness提供选项级操作select()/deselect()先读取aria-selected判断当前状态仅在需要时才调用toggle()点击宿主元素避免多余点击实现是幂等的重复select()不会产生第二次点击。toggle()直接点击宿主元素切换选中态。isSelected()读取aria-selected属性是否为true。getCheckboxPosition(): PromiseMatListOptionTogglePosition返回before或after通过检测.mdc-list-item__start .mdc-checkbox是否存在判断复选框位置。getRadioPosition()同上检测.mdc-list-item__start .mdc-radio。focus()/blur()/isFocused()焦点管理。MatListOptionHarness.with()在getListItemPredicate基础上额外绑定selected选项比较harness.isSelected()与期望值见 selection-list-harness.ts。6.2MatNavListItemHarness导航条目的链接与激活态MatNavListItemHarness见 nav-list-harness.ts特有getHref(): Promisestring | null读取href属性。click()点击宿主元素。isActivated(): Promiseboolean判断是否包含mdc-list-item--activated类当前路由高亮。其with()在getListItemPredicate基础上追加href与activated两个选项的匹配。6.3MatActionListItemHarness操作条目的点击交互MatActionListItemHarness见 action-list-harness.ts提供click()、focus()、blur()、isFocused()与MatNavListItemHarness的交互能力基本一致但没有getHref()/isActivated()。七、实践如何在自己的测试中使用这些 Harness以仓库内的测试 list-harness.spec.ts 为范例使用套路如下。7.1 环境准备在测试模块中导入组件模块与 Harnessimport {TestbedHarnessEnvironment} from angular/cdk/testing/testbed; import {ComponentFixture, TestBed} from angular/core/testing; import {MatListModule} from ../../list; import {MatListHarness, MatListItemHarness} from ./list-harness; beforeEach(async () { fixture TestBed.createComponent(TestComponent); fixture.detectChanges(); const loader TestbedHarnessEnvironment.loader(fixture); listHarness await loader.getHarness( MatListHarness.with({selector: .test-base-list-functionality}), ); });关键步骤是TestbedHarnessEnvironment.loader(fixture)创建环境加载器再通过loader.getHarness(MatListHarness.with(...))获取 Harness 实例with()的selector选项来自BaseHarnessFilters可精确定位页面中多个列表中的目标。7.2 文本断言示例it(should get all items, async () { const items await listHarness.getItems(); expect(await parallel(() items.map(i i.getFullText()))).toEqual([ Item 1, Item 2, Item 3, jasmine.stringMatching(/^Title/), ... ]); }); it(should filter by full text, async () { const items await listHarness.getItems({fullText: /[13]/}); expect(await parallel(() items.map(i i.getFullText()))).toEqual([Item 1, Item 3]); }); it(should filter by title / secondary / tertiary text, async () { const byTitle await listHarness.getItems({title: Title}); const withSecondary await listHarness.getItems({secondaryText: Secondary Text}); const withoutSecondary await listHarness.getItems({secondaryText: null}); const withTertiary await listHarness.getItems({tertiaryText: Tertiary Text}); });注意仓库测试中还演示了{text: /[13]/}与{fullText: /[13]/}行为一致印证了text是fullText的废弃别名。7.3 分组查询示例it(should get items grouped by subheader, async () { const sections await listHarness.getItemsGroupedBySubheader(); expect(sections[0].heading).toBeUndefined(); expect(await parallel(() sections[1].items.map(i i.getFullText()))).toEqual([ Item 2, Item 3, ]); expect(sections[1].heading).toBe(Section 1); });7.4 选择列表交互示例const selectionList await loader.getHarness(MatSelectionListHarness); await selectionList.selectItems({title: Item 1}, {title: Item 3}); const selected await selectionList.getItems({selected: true}); expect(selected.length).toBe(2); await selectionList.deselectItems({title: Item 1}); expect(await (await selectionList.getItems({selected: true})).length).toBe(1);八、深入原理从过滤器到 DOM 的解析链路理解 Harness 的内部机制有助于排查测试定位失败问题。核心调用链如下MatXxxListHarness.with(options)构造HarnessPredicate条目级 Harness 进一步调用getListItemPredicate()用HarnessPredicate.stringMatches()绑定各文本选项list-item-harness-base.ts。getItems(filters)内部执行locatorForAll(this._itemHarness.with(filters))——注意with()传入undefined时会回退为空对象{}各with()默认参数均为{}保证无过滤时依然可用list-harness-base.ts。文本筛选实际作用于 Harness 暴露的读取方法而非直接查询 DOM 文本例如fullText匹配getFullText()返回的“排除图标与头像”的文本title匹配getTitle()的primary-text文本。这意味着过滤与读取永远使用同一套文本口径测试断言与筛选条件天然一致。MatSelectionListHarness.selectItems()通过parallel()并发执行所有匹配条目的select()并对多组过滤器求并集后扁平化selection-list-harness.ts 中的_getItems私有方法。九、版本与兼容性提示依据 API 报告中的deprecated标记与breaking-change注释list-item-harness-base.ts 中标注breaking-change 16.0.0BaseListItemHarnessFilters.text与MatListItemHarnessBase.getText()均已废弃应迁移到fullText/getFullText()报告中的// public注释与仓库的 golden 校验流程goldens/目录与scripts/approve-api-golden.mts绑定任何破坏性 API 变更都必须同步更新 golden 文件并显式标注 breaking-change 版本因此依赖这些 API 的测试代码在升级组件库版本时可以依据 golden 文件的变更记录预判兼容性影响本仓库对应版本的组件实现采用 MDC 样式类如mdc-list-item--with-one-line、mdc-list-item--disabledHarness 的hostSelector与内部选择器均以这些类为准若自行编写替代选择器请以 src/material/list/testing/ 中定义的常量如iconSelector .mat-mdc-list-item-icon、avatarSelector .mat-mdc-list-item-avatar为基准。综上angular/material_list_testing为 Material List 全家族只读、操作、导航、选择四种形态提供了统一且完备的测试抽象。掌握本文梳理的 Harness 继承体系、过滤器字段语义、文本读取规则与分组/选择交互 API你就能写出与官方实现同源、稳定且可维护的组件测试。【免费下载链接】componentsComponent infrastructure and Material Design components for Angular项目地址: https://gitcode.com/GitHub_Trending/co/components创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表