十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

Filament 表格测试完整指南:掌握 Tables 组件的高效断言方法

Filament 表格测试完整指南:掌握 Tables 组件的高效断言方法 Filament 表格测试完整指南掌握 Tables 组件的高效断言方法【免费下载链接】filamentA powerful open-source UI framework for Laravel • Build and ship apps admin panels fast with Livewire项目地址: https://gitcode.com/GitHub_Trending/fi/filament本文基于 Filament 开源仓库filament中 docs/10-testing/03-testing-tables.md 编写。Filament 是构建在 Laravel 与 Livewire 之上的 UI 框架其表格Tables组件是后台面板最核心的模块之一。本文将系统讲解如何为表格编写自动化测试覆盖表格渲染、记录展示、列配置、搜索排序、过滤器、汇总统计与可切换列等场景并深入仓库源码剖析每个断言方法的底层实现帮助你写出可复制、可运行、可维护的表格测试套件。测试前的准备表格测试建立在 Laravel Livewire 的测试基础设施之上。Filament 对表格测试的支持主要由 packages/tables/src/Testing 目录下的几个 Trait 提供它们通过mixin Testable混入 Livewire 的测试对象为断言链补充了丰富的表格专属方法TestsRecords.php记录级断言可见、不可见、数量TestsColumns.php列级断言渲染、存在、状态、排序、搜索等TestsFilters.php过滤器断言启用、重置、移除、可见性TestsSummaries.php汇总统计断言编写测试时统一使用 Pest 的livewire()辅助函数挂载表格页面即可获得完整的链式断言能力。若使用 PHPUnit同样通过Livewire\Livewire::test()调用这些方法但文档示例以 Pest 为准。测试入口可参考 tests/src/Tables/TestCase.php 与仓库内大量真实测试用例如 tests/src/Tables/SummaryTest.php。测试表格能否渲染最简单的冒烟测试是确保表格页面能成功渲染。直接使用 Livewire 自带的assertSuccessful()即可use function Pest\Livewire\livewire; it(can render page, function () { livewire(ListPosts::class) -assertSuccessful(); });ListPosts是PostResource的列表页通常是继承Filament\Resources\Pages\ListRecords的 Livewire 组件。该测试验证页面组件在挂载后没有抛出异常。测试表格展示哪些记录断言可见 / 不可见 / 数量要验证表格当前展示了哪些数据可以使用三个记录级断言方法use function Pest\Livewire\livewire; it(cannot display trashed posts by default, function () { $posts Post::factory()-count(4)-create(); $trashedPosts Post::factory()-trashed()-count(6)-create(); livewire(PostResource\Pages\ListPosts::class) -assertCanSeeTableRecords($posts) -assertCanNotSeeTableRecords($trashedPosts) -assertCountTableRecords(4); });这三个断言的语义分别为断言方法作用assertCanSeeTableRecords($records)断言给定记录出现在表格中assertCanNotSeeTableRecords($records)断言给定记录不出现在表格中assertCountTableRecords($count)断言表格记录总数与给定整数相等从 TestsRecords.php 的源码可以看到其内部机制assertCanSeeTableRecords()会将每条记录Model实例转换为getTableRecordKey()得到的主键拼接成{组件ID}.table.records.{key}形式的 HTML 标记再通过 Livewire 的assertSeeHtml()检查。assertCanNotSeeTableRecords()则检查wire:key{组件ID}.table.records.{key}形式的标记不存在assertDontSeeHtml。assertCountTableRecords()调用组件的getAllTableRecordsCount()方法统计总数并用Assert::assertSame做严格比较。因此这三个断言本质上是基于渲染后的 HTML/DOM 进行验证无需访问数据库查询计数。分页场景如果表格启用了分页assertCanSeeTableRecords()只会检查第一页的记录。要切换到其他页先调用call(gotoPage, 2)livewire(PostResource\Pages\ListPosts::class) -call(gotoPage, 2) -assertCanSeeTableRecords($secondPagePosts);延迟加载场景如果表格调用了deferLoading()数据是异步加载的断言前必须先调用loadTable()手动触发加载livewire(PostResource\Pages\ListPosts::class) -loadTable() -assertCanSeeTableRecords($posts);loadTable()在 TestsRecords.php 中实现本质是$this-call(loadTable)即触发组件上的加载动作。deferLoading()的配置定义位于 CanDeferLoading.php默认为false可通过deferLoading(bool | Closure $condition true)开启。测试列测试列能否渲染要确认某个列确实被渲染到了表格中把列名传给assertCanRenderTableColumn()use function Pest\Livewire\livewire; it(can render post titles, function () { Post::factory()-count(10)-create(); livewire(PostResource\Pages\ListPosts::class) -assertCanRenderTableColumn(title); });该辅助方法会获取这一列在每一行渲染出的 HTML并检查它存在于表格中。从 TestsColumns.php 的实现看它首先断言列可见然后对当前表格的所有记录构造wire:key{组件ID}.table.record.{key}.column.{name}标记并执行assertSeeHtml()。反向断言用assertCanNotRenderTableColumn()use function Pest\Livewire\livewire; it(can not render post comments, function () { Post::factory()-count(10)-create() livewire(PostResource\Pages\ListPosts::class) -assertCanNotRenderTableColumn(comments); });它断言该列对应的 HTML 默认不会出现在表格中内部先assertTableColumnExists()确认列已定义再检查列标记assertDontSeeHtml。测试列搜索通过searchTable()传入搜索关键词然后用记录断言验证过滤结果use function Pest\Livewire\livewire; it(can search posts by title, function () { $posts Post::factory()-count(10)-create(); $title $posts-first()-title; livewire(PostResource\Pages\ListPosts::class) -searchTable($title) -assertCanSeeTableRecords($posts-where(title, $title)) -assertCanNotSeeTableRecords($posts-where(title, !, $title)); });如果想对单个列分别搜索用searchTableColumns()传入「列名 关键词」的数组use function Pest\Livewire\livewire; it(can search posts by title column, function () { $posts Post::factory()-count(10)-create(); $title $posts-first()-title; livewire(PostResource\Pages\ListPosts::class) -searchTableColumns([title $title]) -assertCanSeeTableRecords($posts-where(title, $title)) -assertCanNotSeeTableRecords($posts-where(title, !, $title)); });从 TestsColumns.php 的源码可以看到searchTable()实际是$this-set(tableSearch, $search)searchTableColumns()是$this-set(tableColumnSearches, $searches)——它们通过设置 Livewire 组件上的状态属性来触发搜索与用户在界面上输入的行为完全等价。测试列排序调用sortTable()并传入列名进行排序第二个参数传desc可反转排序方向。排序后使用assertCanSeeTableRecords()的inOrder参数断言记录按顺序渲染use function Pest\Livewire\livewire; it(can sort posts by title, function () { Post::factory()-count(10)-create(); $sortedPostsAsc Post::query()-orderBy(title)-get(); $sortedPostsDesc Post::query()-orderBy(title, desc)-get(); livewire(PostResource\Pages\ListPosts::class) -sortTable(title) -assertCanSeeTableRecords($sortedPostsAsc, inOrder: true) -sortTable(title, desc) -assertCanSeeTableRecords($sortedPostsDesc, inOrder: true); });sortTable()在源码中执行$this-call(sortTable, $name, $direction)与用户点击表头排序的动作路径一致inOrder: true时断言改用assertSeeHtmlInOrder()严格按顺序检查记录标记。注意Filament 表格使用 SQL 的ORDER BY语句在输出前排序。不同数据库驱动的排序策略可能不同也可能与 PHP 自身的排序策略不一致所以测试用例中应当用数据库查询的orderBy()构造期望顺序而不是对模型集合使用sortBy()。测试列的状态assertTableColumnStateSet()与assertTableColumnStateNotSet()用于断言某条记录在某个列上的原始状态即未经过格式化输出的值use function Pest\Livewire\livewire; it(can get post author names, function () { $posts Post::factory()-count(10)-create(); $post $posts-first(); livewire(PostResource\Pages\ListPosts::class) -assertTableColumnStateSet(author.name, $post-author-name, record: $post) -assertTableColumnStateNotSet(author.name, Anonymous, record: $post); });如果列配置了格式化逻辑如formatStateUsing()、日期格式化等则需要使用assertTableColumnFormattedStateSet()与assertTableColumnFormattedStateNotSet()断言格式化后的状态use function Pest\Livewire\livewire; it(can get post author names, function () { $post Post::factory([name John Smith])-create(); livewire(PostResource\Pages\ListPosts::class) -assertTableColumnFormattedStateSet(author.name, Smith, John, record: $post) -assertTableColumnFormattedStateNotSet(author.name, $post-author-name, record: $post); });从 TestsColumns.php 的实现看这两个断言会先通过assertTableRecordKeyExists()确认行存在然后把列绑定到指定记录、调用clearCachedState()清空缓存再分别对比getState()原始状态或formatState($column-getState())格式化状态。注意数组类型的状态会先json_encode再做比较。测试列是否存在assertTableColumnExists()用于确认列已定义在表格中use function Pest\Livewire\livewire; it(has an author column, function () { livewire(PostResource\Pages\ListPosts::class) -assertTableColumnExists(author); });该断言还支持传入一个回调函数作为第二参数对列实例做「真值测试」以校验其具体配置第三参数可传入记录适用于校验结果依赖具体行数据的情况use function Pest\Livewire\livewire; use Filament\Tables\Columns\TextColumn; it(has an author column, function () { $post Post::factory()-create(); livewire(PostResource\Pages\ListPosts::class) -assertTableColumnExists(author, function (TextColumn $column): bool { return $column-getDescriptionBelow() $post-subtitle; }, $post); });源码层面assertTableColumnExists()先通过getTable()-getColumn($name)取列并用Assert::assertInstanceOf(Column::class, ...)校验再执行真值回调TestsColumns.php。测试列的可见性assertTableColumnVisible()与assertTableColumnHidden()用于校验列对用户是否可见对应-hidden()、-visible()等配置use function Pest\Livewire\livewire; it(shows the correct columns, function () { livewire(PostResource\Pages\ListPosts::class) -assertTableColumnVisible(created_at) -assertTableColumnHidden(author); });其内部实现是检查列的isHidden()返回值TestsColumns.php。测试列的描述文本TextColumn可以通过-description()在文本上方或下方附加描述。使用assertTableColumnHasDescription()与assertTableColumnDoesNotHaveDescription()校验位置参数为above或below默认是belowuse function Pest\Livewire\livewire; it(has the correct descriptions above and below author, function () { $post Post::factory()-create(); livewire(PostsTable::class) -assertTableColumnHasDescription(author, Author! ↓↓↓, $post, above) -assertTableColumnHasDescription(author, Author! ↑↑↑, $post) -assertTableColumnDoesNotHaveDescription(author, Author! ↑↑↑, $post, above) -assertTableColumnDoesNotHaveDescription(author, Author! ↓↓↓, $post); });源码根据$position选择比较getDescriptionAbove()或getDescriptionBelow()TestsColumns.php。测试列的额外属性通过-extraAttributes()配置在列元素上输出的额外 HTML 属性可用以下两个断言校验use function Pest\Livewire\livewire; it(displays author in red, function () { $post Post::factory()-create(); livewire(PostsTable::class) -assertTableColumnHasExtraAttributes(author, [class text-danger-500], $post) -assertTableColumnDoesNotHaveExtraAttributes(author, [class text-primary-500], $post); });两个方法分别比较列的getExtraAttributes()是否与给定数组相等/不相等TestsColumns.php。测试 SelectColumn 的选项如果表格使用SelectColumn下拉选择列可以用assertTableSelectColumnHasOptions()与assertTableSelectColumnDoesNotHaveOptions()校验其选项集use function Pest\Livewire\livewire; it(has the correct statuses, function () { $post Post::factory()-create(); livewire(PostsTable::class) -assertTableSelectColumnHasOptions(status, [unpublished Unpublished, published Published], $post) -assertTableSelectColumnDoesNotHaveOptions(status, [archived Archived], $post); });实现上这两个方法会调用SelectColumn的getOptions()与期望数组做相等性比较TestsColumns.php。测试过滤器启用与设置过滤器filterTable()配合记录断言即可验证过滤逻辑use function Pest\Livewire\livewire; it(can filter posts by is_published, function () { $posts Post::factory()-count(10)-create(); livewire(PostResource\Pages\ListPosts::class) -assertCanSeeTableRecords($posts) -filterTable(is_published) -assertCanSeeTableRecords($posts-where(is_published, true)) -assertCanNotSeeTableRecords($posts-where(is_published, false)); });对于简单过滤器Filter::make()这相当于直接启用它。如果需要给SelectFilter或TernaryFilter设置具体值把值作为第二个参数传入use function Pest\Livewire\livewire; it(can filter posts by author_id, function () { $posts Post::factory()-count(10)-create(); $authorId $posts-first()-author_id; livewire(PostResource\Pages\ListPosts::class) -assertCanSeeTableRecords($posts) -filterTable(author_id, $authorId) -assertCanSeeTableRecords($posts-where(author_id, $authorId)) -assertCanNotSeeTableRecords($posts-where(author_id, !, $authorId)); });从 TestsFilters.php 的实现可以看到filterTable()如何按过滤器类型智能构造状态TernaryFilter无第二参数或传true时置为[value true]否则包装为[value $data]SelectFilter单选框包装为[value $data]多选框isMultiple()把值包装为[values [...]]并且传入的Model会被自动转为getKey()、BackedEnum会转为-value其他普通过滤器非数组值被包装为[isActive $data true || $data null]。最终通过$this-set(tableFilters.{$filter-getName()}, $data)设置组件状态与真实界面操作等价。重置过滤器resetTableFilters()将所有过滤器恢复到初始状态use function Pest\Livewire\livewire; it(can reset table filters, function () { $posts Post::factory()-count(10)-create(); livewire(PostResource\Pages\ListPosts::class) -resetTableFilters(); });它调用组件的resetTableFiltersForm()方法TestsFilters.php。移除过滤器removeTableFilter()移除单个过滤器第二参数可指定要清除的字段use function Pest\Livewire\livewire; it(filters list by published, function () { $posts Post::factory()-count(10)-create(); $unpublishedPosts $posts-where(is_published, false)-get(); livewire(PostsTable::class) -filterTable(is_published) -assertCanNotSeeTableRecords($unpublishedPosts) -removeTableFilter(is_published) -assertCanSeeTableRecords($posts); });removeTableFilters()一次性移除所有过滤器use function Pest\Livewire\livewire; it(can remove all table filters, function () { $posts Post::factory()-count(10)-forAuthor()-create(); $unpublishedPosts $posts -where(is_published, false) -where(author_id, $posts-first()-author-getKey()); livewire(PostsTable::class) -filterTable(is_published) -filterTable(author, $author) -assertCanNotSeeTableRecords($unpublishedPosts) -removeTableFilters() -assertCanSeeTableRecords($posts); });两者分别调用组件上的removeTableFilter($filter, $field)与removeTableFilters()动作TestsFilters.php。测试过滤器的可见性assertTableFilterVisible()与assertTableFilterHidden()校验过滤器对用户是否可见对应-hidden()、-visible()use function Pest\Livewire\livewire; it(shows the correct filters, function () { livewire(PostsTable::class) -assertTableFilterVisible(created_at) -assertTableFilterHidden(author); });实现上它们以withHidden: true获取过滤器实例再分别断言isVisible()与isHidden()TestsFilters.php。测试过滤器的存在性assertTableFilterExists()确认过滤器已定义同样支持传入回调对BaseFilter实例做真值测试use function Pest\Livewire\livewire; it(has an author filter, function () { livewire(PostResource\Pages\ListPosts::class) -assertTableFilterExists(author); });use function Pest\Livewire\livewire; use Filament\Tables\Filters\SelectFilter; it(has an author filter, function () { livewire(PostResource\Pages\ListPosts::class) -assertTableFilterExists(author, function (SelectFilter $column): bool { return $column-getLabel() Select author; }); });源码先通过parseTableFilterName()解析过滤器名支持嵌套/带点号的名称再用Assert::assertInstanceOf(BaseFilter::class, ...)校验随后执行真值回调TestsFilters.php。测试汇总Summaries表格列可以通过-summarize()添加汇总统计如平均、总数、范围、求和等。assertTableColumnSummarySet()用于验证汇总计算结果use function Pest\Livewire\livewire; it(can average values in a column, function () { $posts Post::factory()-count(10)-create(); livewire(PostResource\Pages\ListPosts::class) -assertCanSeeTableRecords($posts) -assertTableColumnSummarySet(rating, average, $posts-avg(rating)); });参数说明第一个参数是列名第二个参数是汇总器 IDsummarizer ID第三个参数是期望值。需要重点说明的是值的归一化期望值与实际值在比较前会统一处理例如123.12与123.12视为相同[Fred, Jim]与[Jim, Fred]也视为相同源码使用Assert::assertEqualsCanonicalizing做无序比较并会对数值做round(..., 4)归一化见 TestsSummaries.php。汇总器 ID 通过在make()中传入指定ID 在同一列内的多个汇总器之间必须唯一use Filament\Tables\Columns\Summarizers\Average; use Filament\Tables\Columns\TextColumn; TextColumn::make(rating) -summarize(Average::make(average))只统计当前分页如果要只对当前分页内的记录计算汇总传入isCurrentPaginationPageOnly: trueuse function Pest\Livewire\livewire; it(can average values in a column, function () { $posts Post::factory()-count(20)-create(); livewire(PostResource\Pages\ListPosts::class) -assertCanSeeTableRecords($posts-take(10)) -assertTableColumnSummarySet(rating, average, $posts-take(10)-avg(rating), isCurrentPaginationPageOnly: true); });从源码看该参数决定使用getPageTableSummaryQuery()当前页还是getAllTableSummaryQuery()全部记录作为汇总的查询基础TestsSummaries.php。测试范围汇总器对于Range之类的范围汇总器把最小值和最大值以[$minimum, $maximum]元组数组形式传入use function Pest\Livewire\livewire; it(can average values in a column, function () { $posts Post::factory()-count(10)-create(); livewire(PostResource\Pages\ListPosts::class) -assertCanSeeTableRecords($posts) -assertTableColumnSummarySet(rating, range, [$posts-min(rating), $posts-max(rating)]); });仓库中的真实测试用例覆盖了average、count、range、sum等汇总器及分页变体可参考 tests/src/Tables/SummaryTest.php。测试可切换列表格列默认只有「默认开启切换」的列才会被渲染和测试。toggleAllTableColumns()可以把所有可切换列全部打开use function Pest\Livewire\livewire; it(can toggle all columns, function () { livewire(PostResource\Pages\ListPosts::class) -toggleAllTableColumns(); });传入false则把所有可切换列全部关闭use function Pest\Livewire\livewire; it(can toggle all columns off, function () { livewire(PostResource\Pages\ListPosts::class) -toggleAllTableColumns(false); });从 TestsColumns.php 的实现看它会遍历组件上的tableColumns状态跳过不可切换isToggleable为假的列把isToggled设为指定值然后写入状态并调用applyTableColumnManager应用列管理器的变更。小结Filament 表格测试的断言体系覆盖了从「页面能否渲染」到「列、过滤器、汇总的每一个配置细节」的完整链路。理解其底层机制有助于写出更精准的用例记录级断言基于渲染后的wire:keyHTML 标记天然与 UI 行为对齐搜索、排序、过滤、切换列等操作都通过设置 Livewire 组件状态或调用组件方法实现与真实用户操作路径一致汇总断言对数值与数组做了归一化处理比较时更宽容写期望值时无需担心类型与顺序细节。更全面的表格配置可查阅 packages/tables/docs/01-overview.md 与 docs/03-resources/02-listing-records.md测试体系的其余部分资源、Schema、动作、通知可继续阅读 docs/10-testing/01-overview.md 以及 docs/10-testing/02-testing-resources.md、docs/10-testing/04-testing-schemas.md、docs/10-testing/05-testing-actions.md、docs/10-testing/06-testing-notifications.md组合起来即可为整个后台面板建立完整的回归防线。【免费下载链接】filamentA powerful open-source UI framework for Laravel • Build and ship apps admin panels fast with Livewire项目地址: https://gitcode.com/GitHub_Trending/fi/filament创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表