spatie / tabular-assertions
使用 Pest 或 PHPUnit 编写表格断言
Requires
- php: ^8.2
Requires (Dev)
- laravel/pint: ^1.0
- pestphp/pest: ^2.30
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^10.5
- spatie/ray: ^1.40
README
表格断言允许您以类似 Markdown 表格的格式描述数据,并将其与实际数据进行比较。这对于比较像财务数据或时间序列这样的大型有序数据集非常有用。
使用 Pest
test('it compares a table', function () { $order = Order::factory() ->addItem('Pen', 2) ->addItem('Paper', 1) ->addItem('Pencil', 5) ->create(); expect($order->items)->toMatchTable(' | #id | #order_id | name | quantity | | #1 | #1 | Pen | 2 | | #2 | #1 | Paper | 1 | | #3 | #1 | Pencil | 5 | '); });
使用 PHPUnit
use PHPUnit\Framework\TestCase; use Spatie\TabularAssertions\PHPUnit\TabularAssertions; class PHPUnitTest extends TestCase { use TabularAssertions; public function test_it_contains_users(): void { $order = Order::factory() ->addItem('Pen', 2) ->addItem('Paper', 1) ->addItem('Pencil', 5) ->create(); $this->assertMatchesTable(' | #id | #order_id | name | quantity | | #1 | #1 | Pen | 2 | | #2 | #1 | Paper | 1 | | #3 | #1 | Pencil | 5 | ', $order->items); } }
支持我们
我们投入了大量资源来创建一流的开放式源代码包。您可以通过购买我们的付费产品之一来支持我们。
我们非常感谢您从家乡给我们寄来明信片,并说明您正在使用我们的哪个包。您可以在我们的联系页面找到我们的地址。我们将所有收到的明信片发布在我们的虚拟明信片墙上。
安装
您可以通过 composer 安装此包
composer require spatie/tabular-assertions
为什么选择表格断言?
表格断言相对于其他测试策略有两个主要优点:期望优化了可读性,失败的断言可以一次性显示多个错误。
1. 您可以手动编写包含大量数据且优化了可读性的期望。 基于文本的表格紧凑,允许您在两个维度上比较数据。
另一种选择是编写多个断言。
expect($items[0]['order_id'])->toBe($order->id); expect($items[0]['name'])->toBeDate('Pen'); expect($items[0]['quantity'])->toBe(2); expect($items[1]['order_id'])->toBe($order->id); expect($items[1]['name'])->toBeDate('Paper'); expect($items[1]['quantity'])->toBe(1); // …
期望要求您逐个断言每个属性。这使得您很难一目了然地看到所有日期,并且总体上可读性较差。
关联数组需要大量的标签重复。
expect($items[0])->toBe([ 'order_id' => $order->id, 'name' => 'Pen', 'quantity' => 2, ]); expect($items[1])->toBe([ 'order_id' => $order->id, 'date' => 'Paper', 'quantity' => 1, ]); // …
没有键的数组无法正确对齐(手动维护的空格会被代码风格修复器删除)。当断言具有不同长度的多列时,这会变得不清楚。
expect($items)->toBe([ [$order->id, 'Pen', 2], [$order->id, 'Paper', 1], // … ]);
使用表格断言,我们可以获得数据的紧凑、可读的概述,并且因为它存储在单个字符串中,所以代码风格修复器不会重新格式化它。
expect($items)->toMatchTable(' | #id | #order_id | name | quantity | | #1 | #1 | Pen | 2 | | #2 | #1 | Paper | 1 | | #3 | #1 | Pencil | 5 | ');
2. 可以显示多个问题的错误。 使用单独的期望,测试在第一个失败的断言处失败,这意味着您没有完整的画面(小问题 vs. 一切都坏了)
如果您将两个数据集序列化为表格,当您使用 assertEquals
时,您将得到像 PhpStorm 输出一样的视觉差异。
在这个断言中,您可以一眼看出一个值是错误的,还有一个行缺失。使用单独的断言,您只能看到测试运行器遇到的第一个错误。
这种测试风格在您有大量数据需要断言时表现得尤为出色。这个例子有 9 行和 9 列,这意味着我们在保持可读性的同时比较了 81 个数据点。
expect($order->logs)->toLookLike(" | type | reason | #product_id | #tax_id | #shipping_id | #payment_id | price | paid | refunded | | product | created | #1 | | | | 80_00 | 80_00 | 0_00 | | tax | created | #1 | #1 | | | 5_00 | 5_00 | 0_00 | | tax | created | #1 | #2 | | | 10_00 | 10_00 | 0_00 | | shipping | created | #1 | | #1 | | 5_00 | 5_00 | 0_00 | | product | paid | #1 | | | #1 | 0_00 | 0_00 | 2_00 | | tax | paid | #1 | #1 | | #1 | 0_00 | 0_00 | 0_00 | | tax | paid | #1 | #2 | | #1 | 0_00 | 0_00 | 0_00 | | shipping | paid | #1 | | #1 | #1 | 0_00 | 0_00 | 0_00 | ");
用法
基本用法:Pest
使用 Pest,插件将自动加载并可用。使用自定义的 toMatchTable()
期望来比较表格中的数据。
基本用法:PHPUnit
使用 PHPUnit,将 Spatie\TabularAssertions\PHPUnit\TabularAssertions
特性添加到您想使用表格断言的测试中。使用 $this->assertMatchesTable()
来比较表格中的数据。
动态值
有时,您可能想要比较数据,但实际上并不需要比较确切值。例如,您想要断言每个人都在同一个团队中,但不知道团队ID,因为每次运行时数据都是随机生成的。可以通过在列名前加一个 #
来标记列名为“动态”。动态列将使用占位符替换值。占位符对于列中的值是唯一的。因此,具有ID 123
的团队将始终渲染为 #1
,另一个ID为 456
的团队将使用 #2
等。
例如,Sebastian和Freek在Spatie团队,该团队有一个随机ID,而Christoph在Laravel团队,也有另一个随机ID。
| name | #team_id |
| Sebastian | #1 |
| Freek | #1 |
| Christoph | #2 |
自定义断言
表格断言会将实际值转换为字符串。我们经常处理比可字符串化的数据更复杂的数据,在这种情况下,创建一个自定义断言方法来准备数据是值得的。
考虑以下示例,其中有一个 User
模型,它具有 id
、name
和 date_of_birth
,这些将被转换为 Carbon
对象。
expect(User::all())->toMatchTable(' | id | name | date_of_birth | | 1 | Sebastian | 1992-02-01 00:00:00 | ');
由于 Carbon
对象在字符串化时会自动附加秒,因此我们的表格变得嘈杂。相反,我们将创建一个自定义的 toMatchUsers
断言来在我们断言之前准备我们的数据。
expect()->extend('toMatchUsers', function (string $expected) { $users = $this->value->map(function (User $user) { return [ 'id' => $user->id, 'name' => $user->name, 'date_of_birth' => $user->date_of_birth->format('Y-m-d'), ]; }); expect($users)->toBe($expected); });
expect(User::all())->toMatchTable(' | id | name | date_of_birth | | 1 | Sebastian | 1992-02-01 | ');
在PHPUnit中,这将是一个自定义断言方法。
class UserTest extends TestCase { use TabularAssertions; private function assertMatchesUsers(string $expected, Collection $users): void { $users = $users->map(function (User $user) { return [ 'id' => $user->id, 'name' => $user->name, 'date_of_birth' => $user->date_of_birth->format('Y-m-d'), ]; }); $this->assertMatchesTable($expected, $users); } }
这也可以用于在断言之前进行任何数据转换或截断。另一个例子:first_name
和 last_name
可能是数据库中的单独列,但在断言中可以合并它们,以减少表格中的不必要的空白。
expect(User::all())->toMatchTable(' | id | name | date_of_birth | | 1 | Sebastian De Deyne | 1992-02-01 | ');
expect()->extend('toMatchUsers', function (string $expected) { $users = $this->value->map(function (User $user) { return [ 'id' => $user->id, 'name' => $user->first_name . ' ' . $user->last_name, 'date_of_birth' => $user->date_of_birth->format('Y-m-d'), ]; }); expect($users)->toBe($expected); });
灵感与替代方案
这个想法受到了Jest的启发,Jest允许您使用表格作为数据提供者。
快照测试也与这一点密切相关。但快照并不总是针对可读性进行优化,它们存储在单独的文件中(不是与测试一起),并且很难手动编写(没有TDD)。
测试
使用Pest编写测试。您可以使用Pest的CLI或运行 composer test
来运行测试套件。
composer test
除了测试之外,PhpStan还会进行静态代码分析。使用 composer analyse
来运行PhpStan。
composer analyse
变更日志
有关最近更改的更多信息,请参阅变更日志。
贡献
有关详细信息,请参阅贡献指南。
安全漏洞
有关如何报告安全漏洞的详细信息,请参阅我们的安全策略。
鸣谢
许可
MIT许可(MIT)。有关更多信息,请参阅许可文件。