ryunosuke / phpunit-extension
PHPUnit 流畅接口和自定义断言
v4.2.1
2024-08-04 11:07 UTC
Requires
- php: >=8.0
- phpunit/phpunit: 9.*
Requires (Dev)
- ryunosuke/functions: 2.*
- dev-master
- v4.2.1
- v4.2.0
- v4.1.0
- v4.0.0
- v3.20.2
- v3.20.1
- v3.20.0
- v3.19.0
- v3.18.0
- v3.17.0
- v3.16.0
- v3.15.0
- v3.14.0
- v3.13.1
- v3.13.0
- v3.12.0
- v3.11.0
- v3.10.1
- v3.10.0
- v3.9.0
- v3.8.1
- v3.8.0
- v3.7.1
- v3.7.0
- v3.6.7
- v3.5.0
- v3.4.0
- v3.3.0
- v3.2.0
- v3.1.0
- v3.0.1
- v3.0.0
- v2.0.2
- v2.0.1
- v2.0.0
- v1.2.0
- v1.1.2
- v1.1.1
- v1.1.0
- v1.0.0
This package is auto-updated.
Last update: 2024-09-04 11:22:25 UTC
README
描述
此包添加了流畅接口并提供自定义断言。
- 例如
that('xxx')->isEqual('xxx')
- 例如
that(1)->isInt()->isBetween(1, 9)
- 例如
that('qwe asd zxc')->stringStartsWith('qwe')->stringEndsWith('zxc')
安装
{ "require-dev": { "ryunosuke/phpunit-extension": "dev-master" } }
用法
实际类
简化图表
# e.g. bootstrap.php /** * @template T * @param T $actual * @return \ryunosuke\PHPUnit\Actual|T */ function that($actual) { return new \ryunosuke\PHPUnit\Actual($actual); } // example TestCase class ActualTest extends \PHPUnit\Framework\TestCase { function test_fluent() { # fluent interface // means: assertThat(5, logicalAnd(isType('int'), greaterThanOrEqual(1), lessThanOrEqual(9))); that(5)->isInt()->isBetween(1, 9); } function test_prefixEach() { # "each*" asserts per values (assert AND all values) // means: assertThat(1, greaterThan(0)); assertThat(2, greaterThan(0)); assertThat(3, greaterThan(0)); that([1, 2, 3])->eachGreaterThan(0); } function test_suffixAnyAll() { # "*Any" asserts multiple arguments (assert OR all arguments) // means: assertThat('hello world', logicalOr(stringContains('hello'), stringContains('noexists'))); that('hello world')->stringContainsAny(['hello', 'noexists']); // ignore case (other arguments are normal) that('hello world')->stringContainsAny(['HELLO', 'noexists'], true); # "*All" asserts multiple arguments (assert AND all arguments) // means: assertThat('hello world', logicalAnd(stringContains('hello'), stringContains('world'))); that('hello world')->stringContainsAll(['hello', 'world']); } function test_var_use() { # "var" returns property of original object (non-public access is possible) $object = new \ArrayObject(['x' => 'X', 'y' => 'Y'], \ArrayObject::ARRAY_AS_PROPS); $property = that($object)->var('x'); assertThat($property, equalTo('X')); # "use" returns method's closure of original object (non-public access is possible) $object = new \ArrayObject(['x' => 'X', 'y' => 'Y'], \ArrayObject::ARRAY_AS_PROPS); $method = that($object)->use('getArrayCopy'); assertThat($method(), equalTo(['x' => 'X', 'y' => 'Y'])); } function test_arrayAccess() { # array access returns array's value and actual $array = ['x' => ['y' => ['z' => [1, 2, 3]]]]; // means: assertThat($array['x']['y']['z'], equalTo([1, 2, 3])); that($array)['x']['y']['z']->isEqual([1, 2, 3]); } function test_propertyAccess() { # property access returns property and actual (non-public access is possible) $object = (object) ['x' => 'X']; // means: assertThat($object->x, equalTo('X')); that($object)->x->isEqual('X'); } function test_methodCall() { # method call returns original result and actual (non-public access is possible) $object = new \ArrayObject([1, 2, 3]); // means: assertThat($object->getArrayCopy(), equalTo([1, 2, 3])); that($object)->getArrayCopy()->isEqual([1, 2, 3]); # actual's method prefers to original method $object = new \ArrayObject([1, 2, 3]); // means: assertThat($object, countOf(3)); not: $object->count(); that($object)->count(3); # "callable" returns original method's callable and actual that($object)->callable('count')->isCallable(); // "callable"'s arguments mean method arguments that($object)->callable('setIteratorClass', \stdClass::class)->throws('derived from ArrayIterator'); # "do" invokes original method and actual that($object)->do('count')->isEqual(3); # "__invoke" returns original::__invoke and actual $object = function ($a, $b) { return $a + $b; }; // means: assertThat($object(1, 2), equalTo(3)); that($object)(1, 2)->isEqual(3); } function test_methodCallWithBinding() { # method call by (...[]) returns method's callable of original object with binding (non-public access is possible) $closure = function ($arg) { echo $arg; }; that($closure)->callable('__invoke', 'hoge')->outputEquals('hoge'); that($closure)(...['hoge'])->outputEquals('hoge'); } function test_try() { # "try" is not thrown method call and actual $object = new \ReflectionObject((object) ['x' => 'X']); // returns original result and actual if not thrown that($object)->try('getProperty', 'x')->isInstanceOf(\ReflectionProperty::class); // returns thrown exception and actual if thrown that($object)->try('getProperty', 'y')->isInstanceOf(\ReflectionException::class); } function test_list() { # "list" returns reference argument and actual // means: (fn (&$ref) => $ref = 123)($dummy); assertThat($dummy, equalTo(123)); $dummy = null; that(fn (&$ref) => $ref = 123)($dummy)->list(0)->isEqual(123); } function test_return() { # "return" returns original value $object = new \stdClass(); assertSame($object, that($object)->return()); } function test_eval() { # "eval" asserts directly constraint (variadic arguments OR all arguments) // means: assertThat('x', equalTo('x')); that('x')->eval(equalTo('x')); // means: assertThat('x', logicalOr(equalTo('x'), equalTo('y'), equalTo('z'))); that('x')->eval(equalTo('x'), equalTo('y'), equalTo('z')); } function test_as() { # "as" describes failure text // means: assertThat('x', equalTo('notX'), 'this is failure message'); that('x')->as('this is failure message')->isEqual('notX'); } function test_break() { # "break" mark breakable test (converting Failure to Warning) that('x')->break()->isEqual('notX'); // ...continued this case } function test_and_exit() { # "and" returns latest actual $object = new \ArrayObject(['x' => 'abcX', 'y' => 'abcY'], \ArrayObject::ARRAY_AS_PROPS); // "and" can call as property also as below that($object) ->x->stringStartsWith('abc')->and->stringLengthEquals(4)->exit() ->y->stringStartsWith('abc')->and->stringLengthEquals(4)->exit() ->getArrayCopy()->count(2)->and->hasKey('x'); # but no need to use them as below $that = that($object); $that->getArrayCopy()->count(2)->hasKey('x')->hasKey('y'); $that->x->stringStartsWith('abc')->stringLengthEquals(4); $that->y->stringStartsWith('abc')->stringLengthEquals(4); } function test_declare() { # declare is replaced below at runtime // that(['x', 'y', 'z'])->declare(); that(['x', 'y', 'z'])->is(['x', 'y', 'z']); } }
实际类的返回值或参数可以透明地使用原始方法,如下所示。
class Example { private int $privateField = 0; public function getPrivate() { return $this->privateField; } public function setPrivate(int $field) { $this->privateField = $field; } } class ExampleTest extends \PHPUnit\Framework\TestCase { function test() { // test object $example = that(new Example()); // directry private access $example->privateField = 3; $example->privateField->is(3); // $field is actual $field = $example->getPrivate(); $field->is(3); // but, $field can use to arguments $example->setPrivate($field); } }
自定义约束
内部
别名
\ryunosuke\PHPUnit\Actual::$constraintVariations
在其他约束中搜索变体。
// Disable. Built-in constraints are not called \ryunosuke\PHPUnit\Actual::$constraintVariations['isSame'] = false; // Alias. This ables to use: $actual->isSame('other') \ryunosuke\PHPUnit\Actual::$constraintVariations['isSame'] = IsIdentical::class; // Construct. This ables to use: $actual->isArray() \ryunosuke\PHPUnit\Actual::$constraintVariations['isArray'] = [IsType::class => [IsType::TYPE_ARRAY]]; // Mix. This ables to use: $actual->isNullOrString() \ryunosuke\PHPUnit\Actual::$constraintVariations['isNullOrString'] = [IsNull::class, IsType::class => [IsType::TYPE_STRING]]; // Instance. This ables to use: $actual->lineCount(5) \ryunosuke\PHPUnit\Actual::$constraintVariations['lineCount'] = new class(/* argument is used as default */0) extends \PHPUnit\Framework\Constraint\Constraint { private $lineCount; public function __construct(int $lineCount) { $this->lineCount = $lineCount; } protected function matches($other): bool { return $this->lineCount === (preg_match_all("#\\R#", $other) + 1); } public function toString(): string { return 'is ' . $this->lineCount . ' lines'; } }; // Shorthand instance by closure. This is the same as above \ryunosuke\PHPUnit\Actual::$constraintVariations['lineCount2'] = function ($other, int $lineCount, string $delimiter = "\\R") { return $lineCount === (preg_match_all("#$delimiter#", $other) + 1); };
用户定义
\ryunosuke\PHPUnit\Actual::$constraintNamespaces
在约束命名空间中搜索。
// This ables to use: $actual->yourConstraint() \ryunosuke\PHPUnit\Actual::$constraintNamespaces['your\\namespace'] = 'your/constraint/directory';
// Disable. chain case function call \ryunosuke\PHPUnit\Actual::$functionNamespaces = [];
代码补全
实际类使用 \ryunosuke\PHPUnit\Annotation
特性。如果您在项目空间中声明此类,则启用自定义方法和代码补全。
// e.g. bootstrap.php namespace ryunosuke\PHPUnit { /** * @method \ryunosuke\PHPUnit\Actual isHoge() */ trait Annotation { function isFuga(): \ryunosuke\PHPUnit\Actual { { return $this->eval(new \PHPUnit\Framework\Constraint\IsEqual('fuga')); } } }
这允许使用 $actual->isH(oge)
补全和 $actual->isF(uga)
方法。
或者调用 \ryunosuke\PHPUnit\Actual::generateAnnotation
。此方法通过 $constraintVariations
和 $constraintNamespaces
返回注释。
TestCaseTrait
此特性提供测试实用工具。
- trapThrowable
- 如果指定异常被抛出,则跳过测试。
- restorer
- 重置函数基础值。当未设置时,恢复前值。
- finalize
- 在测试结束时运行闭包。
- rewriteProperty
- 重写私有/受保护属性。当未设置时,恢复前值。
- tryableCallable
- 将私有/受保护方法封装成闭包。并将参数与默认值绑定。
- getEnvOrSkip
- 返回 getenv()。如果没有值,则跳过测试。
- getConstOrSkip
- 返回 constant()。如果没有定义,则跳过测试。
- getClassMap
- 根据 composer 返回所有类 => 文件数组。
- getClassByDirectory
- 按目录返回类名。
- getClassByNamespace
- 按命名空间返回类名。
- emptyDirectory
- 准备临时目录并清理内容。
- backgroundTask
- 异步运行闭包。
- report
- 向测试结果页脚报告消息。
自定义打印器
此包提供进度打印器。仅在失败时输出。在成功时不会输出。
<phpunit printerClass="\ryunosuke\PHPUnit\Printer\ProgressPrinter"> </phpunit>
自定义其他
# e.g. bootstrap.php ryunosuke\PHPUnit\Replacer::insteadOf();
Exporter
此包提供自定义导出器。此导出器在以下方面进行了更改。
- 扩展字符串的最大字符宽度
- 将二进制字符串更改为引号字符串
- 不插入标记换行符
- 将对象标识符从哈希更改为 id
CodeCoverage
此包提供自定义 CodeCoverage。此 CodeCoverage 在以下方面进行了更改。
- 支持
@codeCoverageIgnore
后缀注释- 例如
foo(); // @codeCoverageIgnore because php8.1 only
- 例如
发布
版本控制是语义版本控制。
4.2.1
- [fixbug] 在 php8.2 中修复了错误
- [merge] 3.20.2
4.2.0
- [feature] 添加了 tryableCallable
- [change] 修复了存根生成
4.1.0
- [change] 修复了 ProgressPrinter
4.0.0
- [change] php>=8.0
- [*change] 删除了过时的功能
3.20.2
- [fixbug] 修复了声明转义
3.20.1
- [fixbug] 修复了 Start/End 被忽略
3.20.0
- [feature] 使用兼容的原始类
3.19.0
- [feature] 添加了 finalize
- [feature] 改进了 Traversable
3.18.0
- [feature] 添加了 VALID_DOMAIN/VALID_HOSTNAME 到 IsValid
3.17.0
- [feature] 添加了 getClassMap/getClassByDirectory/getClassByNamespace
- [特性] 添加了 IsTypeOf 约束
3.16.0
- [特性] 添加了 insteadof
- [变更] 废弃了清除全局状态
3.15.0
- [重构] 代码格式和修复检查
- [特性] 为其添加了清除状态
- [修复bug] 修复了约束和方法调用混合的问题
- [修复bug] 将 getXXXOrSkip 改为静态
3.14.0
- [特性] 添加了 TraversableComparator
- [修复bug] 修复了 self/static 类型
- [修复bug] 修复了多次标记文件
3.13.1
- [修复bug] 修复了测试失败时子进程未终止的问题
- [修复bug] 修复了在 Windows 上发现的单引号
3.13.0
- [特性] 添加了报告后的功能
- [特性] generateStub 支持glob模式
3.12.0
- [变更] 在警告测试中抑制了警告
- [特性] 添加了 backgroundTask
- [修复bug] 修复了 mixin 不附加未生成的存根
3.11.0
- [变更] 更改了 ProgressPrinter 格式并支持可中断测试
- [特性] 添加了 trapThrowable
- [特性] 添加了 breakable
- [变更] 废弃了函数调用者
- [重构] 修复了错误的命名空间
3.10.1
- [变更] 改变了存根类的层次结构
- [修复bug] 修复了 __set 不设置祖先私有字段
- [修复bug] 修复了 generateStub 丢失了原始类型
- [修复bug] 修复了 generateStub 忽略了公共成员
3.10.0
- [特性] 改进了 generateStub
- [特性] 添加了 MatchesCountEquals 约束
- [特性] 添加了如果实际参数则解包原始值
- [特性] 添加了禁用功能选项
- [变更] 废弃了使用对象的 __toString 的静态调用
- [修复bug] 修复了由于异常隐式传递而引起的异常
- [修复bug] 修复了文件系统函数拒绝空字符串
- [修复bug] 修复了 __set 私有字段
- [修复bug] 修复了 "debug" 方法始终返回 null
3.9.0
- [变更] 修复了打印器的奇异性
- 提高了可移植性
- 优先指定列
- 启用详细模式
- 在中断时打印结果
3.8.1
- [特性] 标记风险不进行断言
- [特性] 添加了 wasOutputed/wasErrored/inElapsedTime 方法
3.8.0
- [特性] 添加了 restorer
- [特性] 添加了 get(Env|Const)OrSkip
- [变更] 修复了 ExpectationFailedException 消息过大
- [修复bug] 修复了输出被吞没
3.7.1
- [修复bug] 修复了损坏的依赖关系
3.7.0
- [修复bug] 修复了重复的注解
- [特性] 添加了 Is 约束(比 IsEqual 更宽松)
- [特性] 添加了 ClosesTo 约束
- [特性] 添加了 DatetimeEquals 约束
- [特性] 在文件系统中支持 SplFileInfo
- [变更] 将 as 方法更改为可变参数
3.6.0
- [重构] 将私有字段名称更改为与存根生成不兼容
- [特性] 实现了禁用内置约束
- [特性] 添加了 TestCaseTrait 特性
- [特性] 添加了 declare 方法
- [特性] 添加了新方法
- [特性] 添加了 isUndefined 变体
- [特性] 添加了 EqualsPath 约束
- [修复bug] 修复了存根生成中没有 $
- [修复bug] 修复了由于频繁的不必要的函数调用而严格执行
- [修复bug] 修复了在 __callStatic 中未调用原始方法
3.5.0
- [特性] 添加了 htmlMatchesArray 支持样式属性
- [修复bug] 修复了 "try" 语句捕获必要的异常
- [变更] 实现了 __callStatic 省略
3.4.0
- [重构] 修复了注解
- [特性] 添加了 ...[] 语法
- [特性] 添加了 stdout 到结果属性
- [特性] 添加了 htmlMatchesArray 支持类和闭包
- [特性] 添加了 OutputMatches 变体
- [修复bug] 修复了测试目标出错时文件位置在测试代码上
- [修复bug] 修复了进度混乱
3.3.0
- [特性] ProgressPrinter 显示失败时的文件位置
- [特性] htmlMatchesArray 使理解 A 失败时更容易
3.2.0
- [特性] 添加了 bootstrap.php 用于模板
- [特性] 打印实际值
3.1.0
- [特性] 为断言统计添加了 final 方法
- [特性] 在 OutputMatches 约束中添加了 raw 标志
- [特性] 为无方法可调用的函数添加了 fn 方法
- [重构] 建立了自描述类
3.0.1
- [修复bug] 在开发和发布期间供应商目录不同
- [修复bug] 可调用函数在非闭包/对象上抛出异常
3.0.0
- [*变更] 查看日志
2.0.1
- [功能] 支持 PHP8
2.0.0
- [*变更] 查看日志
1.2.0
- [功能] 添加 Annotester 类
- [功能] 添加简写闭包别名
- [功能] 添加 int, float ValidType
- [功能] 添加约束别名 mangle 参数
- [功能] 添加 "and" 属性/方法
- [修复bug] 支持静态属性/方法
- [修复bug] 支持 $compatibleVersion 的次要/修补版本
1.1.2
- [功能] 添加 "InTime" 约束
- [功能] 添加 "callable" 方法
- [变更] 弃用 "catch" 和 "print" 方法
1.1.1
- [修复bug] get/offsetGet 实现泄露
- __get: 使用 stringToStructure
- offsetGet: 访问原始偏移
1.1.0
- [功能] 添加版本控制属性
- [功能] 添加 "prefixIs", "suffixIs" 别名
- [功能] 在 get/offsetGet 中支持 Regex 和 JSONPath 和 JMESPath
- [功能] 实现 "__toString" 方法
- [功能] 添加依赖其他约束
- [功能] 添加 "FileSizeIs" 约束
- [变更] 改变 "Not" 位置(例如 NotFileExists -> FileNotExists)
- "notFileExists" 仍可使用,但将来将被删除
- [变更] 将 "all*" 改名为 "each*"
- "all*" 仍可使用,但将来将被删除
- [修复bug] 规范化目录分隔符
1.0.0
- 发布 1.0.0
- [变更] 极大的变化
- [功能] 添加 "function" 方法
- [功能] 添加 "foreach" 方法
- [功能] 支持 "Throws" 多个参数
0.2.0
- [功能] 添加 "var" 方法
- [功能] 添加 "use" 方法
- [功能] 添加 "print" 方法
- [功能] 添加 "return" 方法
- [功能] 添加 "OutputMatches" 约束
- [变更] 删除 "autoback" 方法
- [变更] 重命名类/方法
0.1.0
- [功能] 添加 "*All" 方法
- [功能] 添加 "try" 方法
- [功能] 添加 "message" 方法
- [功能] 添加 "__invoke" 方法
- [功能] 添加 "file*" 约束
- [功能] 替换为原始 "logical*" 约束
- [功能] 变体添加 "is" 别名
- [功能] 变体支持匿名类
- [修复bug] 变体忽略参数
- [变更] __get/__call 可以访问非公共成员
0.0.0
- 发布
许可
MIT