尊重 / 测试
在尊重,我们对测试非常认真,这就是严肃的测试项目。
This package is not auto-updated.
Last update: 2024-09-14 12:37:48 UTC
README
简介
正如其名,这是我们将单元测试做得令人惊叹的地方。通常情况下,我们需要特定的平台或系统,我们的任务不是孤立的。通常,编写模拟、猴子补丁、polyfills 和 shims 的纯任务比它们旨在测试的工作更麻烦。
为了简化我们的测试流程,这些工具应运而生,现在你也可以从这些劳动中受益,并深入了解为什么我们认为测试如此酷。
随着更多项目的加入,本文档将逐步成形,最终这段话将与空白一起消失。请自由地成为尊重的另一个出色项目的一部分。
反思
无需麻烦即可访问类或对象属性,Reflect
使您能够清楚地了解您是在访问或更改类或对象的属性,或者这些属性是静态的还是实例变量,它们是公共的、私有的还是受保护的。对我们来说,这一切都是一样的。
示例类和对象实例
use Respect\Test\Reflect; class HappyPanda { private $p = 'private'; protected $pr = 'protected'; public $pu = 'public'; } $hp = new HappyPanda();
Reflect::on
要获取 Respect\Test\Reflect
助手的一个实例,请调用静态 on
方法,并提供一个对象或类名字符串。
$reflect = Reflect::on($hp); /** or */ $reflect = Reflect::on('HappyPanda');
getProperty($name)
getProperty
方法将返回命名属性的值。
echo $reflect->getProperty('pu'); // public
但鉴于流畅的接口设计,要使用实例对象从我们的 HappyPanda
获取属性,只需写一行代码即可。
echo Reflect::on($hp)->getProperty('p'); // private
我们可以用同样的方式只使用类名。
echo Reflect::on('HappyPanda')->getProperty('pr'); // protected
setProperty($name, $value)
所以你想更改属性,对吧?这就是测试的全部意义所在。
$reflect->setProperty('pu', 1234); echo $reflect->getProperty('pu'); // 1234
或者通过链式调用,让您再次可以在一行中组合所有内容。
echo Reflect::on($hp)->setProperty('p', 'owned')->getProperty('p'); // owned
我们可以用同样的方式只使用类名,我告诉过你什么。
echo Reflect::on('HappyPanda')->setProperty('pu', 'easy')->getProperty('pr'); // easy
getInstance()
正如你可能知道的,我们需要一个实例(对象)来修改这些实例属性,因此您可能最终想要获取该实例本身。无论您的类是抽象的、没有构造函数、带有必需参数的构造函数,还是标记为私有或受保护的,Reflect
都会确保您无论付出什么代价都能获取一个实例。
让我们看看一个新的类定义,它有一个需要 2 个非可选参数的私有构造函数。我们还加载了一些静态属性,但对我们来说,现在这些都一样。
class Panda { private $p = 'private'; protected $pr; public $pu; private static $ps; protected static $prs; public static $pus; private function __construct($a, $b) { } }
最大限度地利用流畅的接口!
$object = Reflect::on('Panda') ->setProperty('p', 1) ->setProperty('pr', 2) ->setProperty('pu', 3) ->setProperty('ps', 4) ->setProperty('prs',5) ->setProperty('pus',6) ->getInstance();
这将为您提供一个具有每个属性都已更改的新 Panda
类实例,并将其分配给变量 $object。看起来像这样
class Panda { private $p = 1; protected $pr = 2; public $pu = 3; private static $ps = 4; protected static $prs = 5; public static $pus = 6; private function __construct($a, $b) { } }
让我们把事情做得更复杂一点,如何关于一个抽象类?当你对抽象类进行反射时,事情会有所不同。我们无法拥有一个抽象类的实例,这是不可能的。让我们看看会发生什么。
让我们使 Happy Panda 成为抽象的
abstract class Panda
{
private $val = 'private';
abstract protected function eatBamboo(array $sticks=array());
private function __construct(&$a, $b)
{
$a++;
$this->val = $b;
}
}
我们对抽象类进行反射,并检索实例。
$object = Reflect::on('Panda')->getInstance();
Reflect 将为您生成一个 Mock 类,这样您就可以有一个有效的抽象类实例进行测试。Mock 类将位于相同的命名空间中(如果适用),并在类名前加上 "Mock"。
class MockPanda extends Panda { public function eatBamboo($sticks=array ()) { } }
如您所见,我们可以从私有构造函数中获取抽象类的实例,但如果我们想执行该构造函数呢?没问题,只需传递一个参数数组给 getInstance()
,Reflect 将为您调用私有构造函数。 注意:示例使用 PHP 5.4 中引入的新数组简写,而 PHP 5.3 使用 array()
而不是 [ ]
echo Reflect::on('Panda')->getProperty('val'); // private $object = Reflect::on('Panda')->getInstance([&$a, 'New Value']); echo Reflect::on('Panda')->getProperty('val'); // New Value echo $a; // 1 $object = Reflect::on('Panda')->getInstance([&$a, 'New Value']); echo $a; // 2 $object = Reflect::on('Panda')->getInstance([&$a, 'New Value']); echo $a; // 3
StreamWrapper
PHP 手册中提到,关于 StreamWrapper,请注意。
这并不是一个真正的类,而是一个定义自己协议的类的原型。
如果您也同意,这很糟糕。难道不更简单直接有一个这个 真正的 类吗?好吧,我们也是这样想的,这就是它,StreamWrapper,别无他名。
与难以驾驭的接口斗争,文档稀少,实现特定且紧密地嵌入到 PHP 解释器的每个方面。那些日子终于结束了。
无需努力,无缝集成到内置默认流包装器 (file://
),用作测试中的文件系统模拟,不再流泪。创建、修改、移动、删除、链接到,从默认数据协议的便利性中进行所需的一切,与物理对应物完全协同,您将无法分辨它们。
如果还不够酷,那么怎样呢?
- 可配置的虚拟文件,数据来自 PHP 字符串变量。
- 读写查找虚拟文件,与真实文件无法区分。
- 无路径限制,我们将为您填写目录。
- 准确使用标准 stat 功能,如验证存在、查询类型、打开资源进行读取、写入、修改,甚至将虚拟文件添加到现有文件夹中。
- 零配置自我管理,无需您做任何事情。
- 零维护,因为它会在自己清理后结束。
- 最小开销,因为它只在预期的地方干预。
- 使用起来如此简单,您会忘记它甚至在那里。
清单可以一直继续下去,但您最好亲自看看。
如何使用 StreamWrapper
只需将库添加到您的包含路径中,配置几个文件(或根本不添加文件)以注入到虚拟文件系统中,我们就完成了。
其余的将由您负责。
我们追求简单的设计,结果是 两个方法 的接口。
StreamWrapper::setStreamOverrides($virtual_fs)
第一个可用方法 setStreamOverrides
允许您通过将 path
映射到 contents
来配置启动文件系统。
文件内容可以是简单的字符串,也可以是映射整个资源作为内容提供者的复杂结构。
StreamWrapper::setStreamOverrides(array( 'virtual/foo-bar-baz.ini' => $my_foo_here_doclet, 'virtual/happy-panda.ini' => "panda=happy\nhappy=panda", 'virtual/custom-stream.ini'=> fopen('data:text/plain;base64,'. urlencode('Sweet like a lemon'), 'wb'), 'virtual/custom-stream-base64.ini'=> fopen('data:text/plain;base64,'. base64_encode('Sweet like a lemon'), 'wb'), ));
StreamWrapper 会处理自己的事情,所以您不需要。
一旦 PHP 脚本运行结束,StreamWrapper 将释放其资源,并在退出时优雅地消失。
您不需要做任何事情。
要启用无启动文件的裸骨虚拟文件系统,只需使用空数组。
StreamWrapper::setStreamOverrides(array());
您可以重复此过程,每次 StreamWrapper 都将清除当前状态,并展示新的配置文件系统,同时确保我们之前使用的资源得到适当的释放。
在我们讨论下一个和最终的 接口方法 # 2 之前,让我们快速看看我们有什么。
如常的 I/O
可以使用您最喜欢的 PHP 文件系统 函数创建额外的虚拟项目,例如使用 mkdir
创建新目录或使用 file_put_contents
填充新文件。
要创建一个包含字符串 "文件将在任何位置创建并可用"
的文本文件,并将其内容保存到当前工作目录相对路径的 it/doesnt/matter/if/path/not/exist.txt
。
file_put_contents('it/doesnt/matter/if/path/not/exist.txt', 'The file will be created and accessible at the location'); print_r(file('it/doesnt/matter/if/path/not/exist.txt'));
Array
(
[0] => The file will be created and accessible at the location
)
但您还可以访问所有中间目录,它们是完全可遍历的。
让我们尝试这个简单的递归。
function traverse($path) { echo "$path \$\n": foreach (new DirectoryIterator($path) as $i) { echo $i->getBasename(), PHP_EOL; if ($i->isDir()) traverse($i->getPathname()); else break; } } traverse('it');
看看我们得到了什么?
it $
.
..
doesnt
it/doesnt $
.
..
matter
it/doesnt/matter $
.
..
if
it/doesnt/matter/if $
.
..
path
it/doesnt/matter/if/path $
.
..
not
it/doesnt/matter/if/path/not $
.
..
exist.txt
这听起来太完美了,当然,让我们看看外壳在PHP之外会说什么。
$ ls it
ls: it: No such file or directory
用法与之前没有变化。
对于任何新内容,StreamWrapper将创建虚拟资源,并使它们透明地可用,就像它们是真实的一样。
否则,一切照旧,回退到标准 file://
流包装协议的内置功能,允许像以前一样访问物理资源。
var_export(scandir('tests'));
array ( 0 => '.', 1 => '..', 2 => 'bootstrap.php', 3 => 'library', 4 => 'phpunit.xml', )
echo file_get_contents('tests/phpunit.xml');
<!-- a Courtesy of Respect/Foundation -->
<phpunit backupGlobals="false"
backupStaticAttributes="false"
bootstrap="bootstrap.php"
....
StreamWrapper::releaseOverrides()
在虚拟世界中会话结束后,您可能想恢复正常,这就是 releaseOverrides
方法的作用。
StreamWrapper::releaseOverrides();
我们简单地释放引用句柄,并允许StreamWrapper以自己的速度开始清理过程。
如果您发现它太长时间才允许您再次使用默认文件系统,您可以坚持立即通过调用PHP函数来恢复标准流包装器
stream_wrapper_restore('file');
但这不是必需的,所有东西最终都会恢复正常。
免责声明
这个源代码是最新发布的,尽管它已经工作,但积极开发可能会再次引发一些火花。我们当然没有探索所有边缘情况,您可能还会发现尚未发现的错误。
如果您读到这儿,您就知道您找到了测试的圣杯,这是真的。
请帮助我们,通过报告任何问题或提出建议,如果它还没有做到您希望的那样精确。这是提出这些想法并看到它们实现的最佳时机。
现在正在接受问题和拉取请求...