phrozenbyte / phpunit-array-asserts
提供各种与数组相关的 PHPUnit 断言,主要用于 API 测试。
Requires
- php: ^7.2 || ^8.0
- phplucidframe/console-table: ^1.2
- phrozenbyte/phpunit-throwable-asserts: ^1.0
Requires (Dev)
- incompass/coverage: ^1.0
- mockery/mockery: ^1.3
- php-parallel-lint/php-parallel-lint: ^1.2
- phpunit/phpunit: ^8
- psalm/plugin-mockery: ^0.7.0
- psalm/plugin-phpunit: ^0.15.1
- symfony/yaml: ^4.4
- vimeo/psalm: ^4.5
Conflicts
- phpunit/phpunit: <8.0
This package is auto-updated.
Last update: 2024-09-14 02:43:13 UTC
README
PHPUnitArrayAssertions
是一个小的 PHPUnit 扩展,用于改进 PHP 数组和类似数组的测试。它引入了 AssociativeArray
、ArrayHasKeyWith
、SequentialArray
和 ArrayHasItemWith
约束。它通常用于 API 测试,以断言 API 结果是否符合某些标准——无论是其结构还是数据。
此 PHPUnit 扩展允许开发者在单个断言中测试结构和数据,使测试用例更加简洁易懂。在某种程度上,它是 PHPUnit 8 中弃用并在 PHPUnit 9 中删除的 ArraySubset
约束的替代品——更强大且更易于理解。有关更多信息,请参阅下面的 “用法”部分 和 “示例”部分。
您需要更多 PHPUnit 约束?请查看 PHPUnitThrowableAssertions
!它引入了 assertCallableThrows()
和 assertCallableThrowsNot()
断言,以改进对异常和 PHP 错误的测试。它比 PHPUnit 核心中的 expectException()
方法更强大、更灵活。
由 Daniel Rudolf 用 ❤️ 制作。 PHPUnitArrayAssertions
是免费的开源软件,根据 MIT 许可证 发布。
目录
安装
PHPUnitArrayAssertions
可在 Packagist.org 上找到,并可以使用 Composer 进行安装
composer require --dev phrozenbyte/phpunit-array-asserts
此 PHPUnit 扩展最初是为 PHPUnit 8 编写的,但应与任何后续的 PHPUnit 版本兼容。如果不兼容,请勿犹豫,在 GitHub 上打开 新问题,或者更好的是,创建一个包含提议修复的 Pull Request。
用法
使用 PHPUnitArrayAssertions
有三种(基本上等效)选项
- 通过使用静态 类
PhrozenByte\PHPUnitArrayAsserts\Assert
- 通过在您的测试用例中使用 特质
PhrozenByte\PHPUnitArrayAsserts\ArrayAssertsTrait
- 通过创建新的 约束实例 (
PhrozenByte\PHPUnitArrayAsserts\Constraint\…
)
所有选项都做同样的事情,唯一的区别是静态类和特质都为无效参数抛出 PHPUnit\Framework\InvalidArgumentException
异常。创建新的约束实例对于高级断言很有用,例如与 PHPUnit\Framework\Constraint\LogicalAnd
一起使用。
约束 AssociativeArray
AssociativeArray
约束断言一个值是一个符合给定结构的关联数组,并且该数组的项通过其他约束。
任何本地数组和 ArrayAccess
对象都被视为关联数组,无论它们使用哪个键。但是,数组的项将应用于匹配约束(参数 $constraints
)。默认情况下,缺失的项将导致约束失败(参数 $allowMissing
,默认为 false
)。默认情况下将忽略额外的项(参数 $allowAdditional
,默认为 true
)。如果您希望存在额外的项时约束失败,将此选项设置为 true
,但是请注意,这仅适用于本地数组。预期的键和要应用的约束,以及缺失和/或额外的项是否应该导致约束失败,都通过构造函数传递。约束可以是任意的 Constraint
实例(例如 PHPUnit\Framework\Constraint\StringContains
),或者任何静态值,要求值完全匹配。
ArrayAssertsTrait
特性为 AssociativeArray
约束公开了两个公共方法:使用 ArrayAssertsTrait::assertAssociativeArray()
执行断言,并使用 ArrayAssertsTrait::associativeArray()
创建 AssociativeArray
约束的新实例。
用法
// using `\PhrozenByte\PHPUnitArrayAsserts\ArrayAssertsTrait` trait ArrayAssertsTrait::assertAssociativeArray( array $constraints, // an array with the expected keys and constraints to apply array|ArrayAccess $array, // the associative array to check bool $allowMissing = false, // whether missing items fail the constraint bool $allowAdditional = true, // whether additional items fail the constraint string $message = '' // additional information about the test ); // using new instance of `\PhrozenByte\PHPUnitArrayAsserts\Constraint\AssociativeArray` new AssociativeArray( array $constraints, bool $allowMissing = false, bool $allowAdditional = true );
示例
$data = [ 'id' => 42, 'name' => 'Arthur Dent', 'options' => [ 'has_towel' => true, 'panic' => false ], ]; // asserts that `$data` is an associative array with exactly the keys: // - "id" with a numeric value, // - "name" with the value "Arthur Dent", and // - "options" with another associative array with the key "panic", whose value must be a boolean $this->assertAssociativeArray([ 'id' => $this->isType(IsType::TYPE_INT), 'name' => 'Arthur Dent', 'options' => $this->associativeArray([ 'panic' => $this->isType(IsType::TYPE_BOOL) ], true) ], $data);
调试
$data = [ 'answer' => 21 /* half the truth */ ]; $this->assertAssociativeArray([ 'answer' => 42 ], $data); // Will fail with the following message: // // Failed asserting that associative array matches constraints. // +----------+-------+----------------------+ // | Key | Value | Constraint | // +----------+-------+----------------------+ // | 'answer' | 21 | Value is equal to 42 | // +----------+-------+----------------------+ // [ ] Allow missing; [x] Allow additional
约束 ArrayHasKeyWith
ArrayHasKeyWith
约束 断言数组具有给定的键,并且其值通过另一个约束。
接受本地数组和 ArrayAccess
对象。如果数组中不存在键(参数 $key
),则约束(参数 $constraint
)将失败。项目的键和值必须通过的约束通过构造函数传递。约束可以是任意的 Constraint
实例(例如 PHPUnit\Framework\Constraint\StringContains
),或者任何静态值,要求值完全匹配。
ArrayAssertsTrait
特性为 ArrayHasKeyWith
约束公开了两个公共方法:使用 ArrayAssertsTrait::assertArrayHasKeyWith()
执行断言,并使用 ArrayAssertsTrait::arrayHasKeyWith()
创建 ArrayHasKeyWith
约束的新实例。
用法
// using `\PhrozenByte\PHPUnitArrayAsserts\ArrayAssertsTrait` trait ArrayAssertsTrait::assertArrayHasKeyWith( string|int $key, // the key of the item to check Constraint|mixed $constraint, // the constraint the item's value is applied to array|ArrayAccess $array, // the array to check string $message = '' // additional information about the test ); // using new instance of `\PhrozenByte\PHPUnitArrayAsserts\Constraint\ArrayHasKeyWith` new ArrayHasKeyWith( string|int $key, Constraint|mixed $constraint );
示例
$data = [ 'id' => 42, 'name' => 'Arthur Dent', 'options' => [ 'has_towel' => true, 'panic' => false ], ]; // asserts that $data has the item `name` with the value "Arthur Dent" $this->assertArrayHasKeyWith('name', 'Arthur Dent', $data);
调试
$data = []; $this->assertArrayHasKeyWith('answer', 42, $data); // Will fail with the following message: // // Failed asserting that Array &0 () is an array that // has the key 'answer' whose value is equal to 42.
约束 SequentialArray
SequentialArray
约束 断言一个值类似于顺序数组,具有最小和/或最大项数,并且所有项都通过另一个约束。
顺序数组定义为以零开始的递增数字键的有序列表。这对于像 [ "foo", "bar" ]
这样的本地顺序数组尤其如此。空数组也视为有效。预期的最小(参数 $minItems
,默认为 0
)和/或最大(参数 $maxItems
,默认为 null
,表示无限)项数,以及应用所有项的约束(可选参数 $constraint
),通过构造函数传递。约束可以是任意的 Constraint
实例(例如 PHPUnit\Framework\Constraint\StringContains
),或者任何静态值,要求值完全匹配。可以通过将参数 $ignoreKeys
设置为 true
(默认为 false
)来禁用要求顺序键,这将导致约束仅检查所需项数以及它们是否与给定的约束匹配。
此约束将完全遍历任何给定的 Traversable
对象。它期望 Traversable
是可重置的。对于 NoRewindIterator
实例,它假定迭代器仍处于初始状态。Generator
将被完全耗尽;如果迭代器已经开始,则对象被视为无效。如果给定 Iterator
,它将尝试将对象的指针恢复到其之前的状态。对于 NoRewindIterator
实例,这将静默失败。对于具有非唯一键的 Iterator
的行为是未定义的。
ArrayAssertsTrait
特性为 SequentialArray
约束公开了两个方法:使用 ArrayAssertsTrait::assertSequentialArray()
进行断言,以及使用 ArrayAssertsTrait::sequentialArray()
创建一个新的 SequentialArray
约束实例。
用法
// using `\PhrozenByte\PHPUnitArrayAsserts\ArrayAssertsTrait` trait ArrayAssertsTrait::assertSequentialArray( array|Traversable $array, // the sequential array to check int $minItems, // required minimum number of items int $maxItems = null, // required maximum number of items (pass null for infinite) Constraint|mixed $constraint = null, // optional constraint to apply all items to bool $ignoreKeys = false, // whether to ignore non-sequential keys string $message = '' // additional information about the test ); // using new instance of `\PhrozenByte\PHPUnitArrayAsserts\Constraint\SequentialArray` new SequentialArray( int $minItems = 0, int $maxItems = null, Constraint|mixed $constraint = null, bool $ignoreKeys = false );
示例
$data = [ "The Hitchhiker's Guide to the Galaxy", "The Restaurant at the End of the Universe", "Life, the Universe and Everything", "So Long, and Thanks for All the Fish", "Mostly Harmless", "And Another Thing...", ]; // asserts that `$data` is a non-empty sequential array with non-empty items $this->assertSequentialArray($data, 1, null, $this->logicalNot($this->isEmpty()));
调试
$data = []; $this->assertSequentialArray($data, 4, null, $this->is(IsType::TYPE_STRING)); // Will fail with the following message: // // Failed asserting that Array &0 () is is a sequential array // with ≥ 4 items matching the constraint "is of type "string"".
约束 ArrayHasItemWith
ArrayHasItemWith
约束 断言数组在给定索引处有一个项,并且其值满足另一个约束。
接受本地数组以及 Traversable
对象。如果数组中的项少于所需项数,则约束将失败。要检查的项的索引(参数 $index
)以及其值必须满足的约束(参数 $constraint
)在构造函数中传递。约束可以是任意的 Constraint
实例(例如 PHPUnit\Framework\Constraint\StringContains
),或者任何静态值,需要与值完全匹配。
此约束将完全遍历任何给定的 Traversable
对象。它期望 Traversable
是可重置的。对于 NoRewindIterator
实例,它假定迭代器仍处于初始状态。Generator
将被完全耗尽;如果迭代器已经开始,则对象被视为无效。如果给定 Iterator
,它将尝试将对象的指针恢复到其之前的状态。对于 NoRewindIterator
实例,这将静默失败。对于具有非唯一键的 Iterator
的行为是未定义的。
ArrayAssertsTrait
特性为 ArrayHasItemWith
约束公开了两个公共方法:使用 ArrayAssertsTrait::assertArrayHasItemWith()
进行断言,以及使用 ArrayAssertsTrait::arrayHasItemWith()
创建一个新的 ArrayHasItemWith
约束实例。
用法
// using `\PhrozenByte\PHPUnitArrayAsserts\ArrayAssertsTrait` trait ArrayAssertsTrait::assertArrayHasItemWith( int $index, // the index of the item to check Constraint|mixed $constraint, // the constraint the item's value is applied to array|Traversable $array, // the array to check string $message = '' // additional information about the test ); // using new instance of `\PhrozenByte\PHPUnitArrayAsserts\Constraint\ArrayHasItemWith` new ArrayHasItemWith( int $index, Constraint|mixed $constraint );
示例
$data = [ '1979-10-12' => "The Hitchhiker's Guide to the Galaxy", '1980-10-00' => "The Restaurant at the End of the Universe", '1982-08-00' => "Life, the Universe and Everything", '1984-11-09' => "So Long, and Thanks for All the Fish", '1992-00-00' => "Mostly Harmless", '2009-10-12' => "And Another Thing...", ]; // asserts that `$data` contains "Life, the Universe and Everything" as third item (i.e. at index 2) $this->assertArrayHasItemWith(2, "Life, the Universe and Everything");
调试
$data = []; $this->assertArrayHasItemWith(2, 'Arthur Dent', $data); // Will fail with the following message: // // Failed asserting that Array &0 () is an array that // has a value at index 2 which is equal to 'Arthur Dent'.
示例
以下是一个(或多或少)真实的 PHPUnitArrayAssertions
示例。查看 testWithPHPUnitArrayAsserts()
方法,了解如何测试复杂的 API 响应。要比较仅使用 PHPUnit 核心功能的实现,请查看 testWithoutPHPUnitArrayAsserts()
方法。没有 PHPUnitArrayAssertions
,您将得到 17 行相当重复的代码,使用这个 PHPUnit 扩展,您可以用 7 行易于理解的代码来测试响应。
<?php declare(strict_types=1); namespace YourName\YourProject\Tests; use PHPUnit\Framework\Constraint\IsType; use PHPUnit\Framework\TestCase; use PhrozenByte\PHPUnitArrayAsserts\ArrayAssertsTrait; class MyTest extends TestCase { use ArrayAssertsTrait; public function testWithPHPUnitArrayAsserts(): void { // 7 lines of easy to understand code to check the API response *with* PHPUnitArrayAsserts // implement your test, the result is stored in $responseData $responseData = [ 'users' => [ [ 'id' => 42, 'name' => 'Arthur Dent', 'options' => [ 'has_towel' => true, 'panic' => false ], ], ] ]; $this->assertArrayHasKeyWith('users', $this->sequentialArray(1), $responseData); $this->assertAssociativeArray([ 'id' => $this->isType(IsType::TYPE_INT), 'name' => 'Arthur Dent', 'options' => $this->associativeArray([ 'panic' => $this->isType(IsType::TYPE_BOOL) ]) ], $responseData['users'][0]); } public function testWithoutPHPUnitArrayAsserts(): void { // 17 lines of pretty repetitive code to check the API response *without* PHPUnitArrayAsserts // implement your test, the result is stored in $responseData $responseData = [ 'users' => [ [ 'id' => 42, 'name' => 'Arthur Dent', 'options' => [ 'has_towel' => true, 'panic' => false ], ], ] ]; $this->assertArrayHasKey('users', $responseData); $this->assertIsArray($responseData['users']); $this->assertGreaterThanOrEqual(1, count($responseData['users'])); // won't work for Traversable $userData = $responseData['users'][0]; // we can't really rely on the existence of key "0" here :/ $this->assertArrayHasKey('id', $userData); $this->assertIsInt($userData['id']); $this->assertArrayHasKey('name', $userData); $this->assertSame('Arthur Dent', $userData['name']); $this->assertArrayHasKey('options', $userData); $this->assertIsArray($userData['options']); $this->assertArrayHasKey('panic', $userData['options']); $this->assertIsBool($userData['options']['panic']); } }