eloquent / phony
PHP 的模拟、存根和间谍工具。
Requires
- php: ^7.3 || ^8
Requires (Dev)
- ext-pdo: *
- eloquent/code-style: ^1.0
- eloquent/phpstan-phony: ^0.7
- errors/exceptions: ^0.2
- friendsofphp/php-cs-fixer: ^2
- hamcrest/hamcrest-php: ^2
- phpstan/extension-installer: ^1
- phpstan/phpstan: ^0.12
- phpstan/phpstan-phpunit: ^0.12
- phpunit/phpunit: ^9
- dev-main / 5.1.x-dev
- 5.0.2
- 5.0.1
- 5.0.0
- 4.0.1
- 4.0.0
- 3.2.0
- 3.1.0
- 3.0.1
- 3.0.0
- 2.0.1
- 2.0.0
- 1.0.1
- 1.0.0
- 0.14.7
- 0.14.6
- 0.14.5
- 0.14.4
- 0.14.3
- 0.14.2
- 0.14.1
- 0.14.0
- 0.13.5
- 0.13.4
- 0.13.3
- 0.13.2
- 0.13.1
- 0.13.0
- 0.12.0
- 0.11.0
- 0.10.2
- 0.10.1
- 0.10.0
- 0.9.0
- 0.8.0
- 0.7.0
- 0.6.4
- 0.6.3
- 0.6.2
- 0.6.1
- 0.6.0
- 0.5.2
- 0.5.1
- 0.5.0
- 0.4.0
- 0.3.0
- 0.2.1
- 0.2.0
- 0.1.1
- 0.1.0
- dev-named-arg-matcher-verification
- dev-named-arguments-redux
- dev-named-arguments
- dev-videos
This package is auto-updated.
Last update: 2023-08-08 04:03:22 UTC
README
不再维护
此包不再维护。有关更多信息,请参阅此声明。
PHP 的模拟、存根和间谍工具。
安装
作为各种 Composer 包提供,具体取决于使用的测试框架
- 对于 Kahlan,请使用 eloquent/phony-kahlan 并导入
Eloquent\Phony\Kahlan
。 - 对于 PHPUnit,请使用 eloquent/phony-phpunit 并导入
Eloquent\Phony\Phpunit
。 - 对于 Peridot,请使用 eloquent/phony-peridot 并导入
Eloquent\Phony
。 - 对于其他框架或独立使用,请使用 eloquent/phony 并导入
Eloquent\Phony
。
文档
完整的文档可供查阅。
Phony 是什么?
Phony 是一个用于创建各种类型的测试替身的 PHP 库,包括对象 模拟、函数 存根和函数 间谍。
为什么使用 Phony?
对旧版和新版语言特性的支持
Phony 承诺支持新出现的 PHP 功能。需要在其他模拟框架中采取变通方法的特性(如按引用传递的参数),通常在 Phony 中“正常工作”。
Phony 支持以下特性:
- 函数级别的存根和间谍,在许多情况下消除了对对象模拟的需求
- 生成器存根和验证
- 特性模拟
- 具有返回类型的函数和方法存根
- 使用
...
符号的现代 可变长度参数列表 - 通过引用传递参数的设置,以及一般性的引用参数
零配置集成
将 Phony 集成到您喜欢的测试框架中就像 选择正确的命名空间导入 一样简单。互补功能使集成无缝且直观
对与其他测试框架的更好集成感兴趣?我们也是!如果有什么我们可以做的,请打开一个 GitHub 问题。
精炼的验证输出
Phony 在其 验证输出 方面进行了大量的工作和完善
Phony 的输出包括简洁但详细的信息,旨在帮助您尽快找到失败的原因。在适当的情况下,输出使用 差异比较和颜色 来进一步突出重要细节。
函数和回调的一等支持
Phony 设计用于支持测试基于对象和基于函数的系统。在 Phony 中,对象模拟建立在功能级全功能的 存根 和 间谍 之上。
这种对支持过程式编程的关注使 Phony 能够处理仅基于类模拟框架无法处理的许多情况。由于 Phony 的类级支持基于其功能级支持,接口是一致的,并且使用时不需要额外了解很多知识。
广泛的功能集
Phony 拥有广泛而强大的功能集。有关特定功能的详细信息,请选择以下选项之一
帮助
对于困难的测试场景的帮助、有关如何使用Phony的问题或报告Phony本身的问题,请打开GitHub问题,以便其他人也能从中受益。
用法
有关详细用法,请参阅文档。
示例测试套件
请参阅phony-examples存储库。
独立使用
安装eloquent/phony包,然后
use function Eloquent\Phony\mock; $handle = mock('ClassA'); $handle->methodA->with('argument')->returns('value'); $mock = $handle->get(); assert($mock->methodA('argument') === 'value'); $handle->methodA->calledWith('argument');
Kahlan使用方法
安装eloquent/phony-kahlan包,然后
use function Eloquent\Phony\Kahlan\mock; describe('Phony', function () { it('integrates with Kahlan', function () { $handle = mock('ClassA'); $handle->methodA->with('argument')->returns('value'); $mock = $handle->get(); expect($mock->methodA('argument'))->toBe('value'); $handle->methodA->calledWith('argument'); }); });
eloquent/phony-kahlan包还提供了自动装配的模拟
use function Eloquent\Phony\Kahlan\on; describe('Phony for Kahlan', function () { it('supports auto-wiring', function (ClassA $mock) { $handle = on($mock); $handle->methodA->with('argument')->returns('value'); expect($mock->methodA('argument'))->toBe('value'); $handle->methodA->calledWith('argument'); }); });
Peridot使用方法
安装eloquent/phony-peridot包,然后
use function Eloquent\Phony\mock; describe('Phony', function () { it('integrates with Peridot', function () { $handle = mock('ClassA'); $handle->methodA->with('argument')->returns('value'); $mock = $handle->get(); expect($mock->methodA('argument'))->to->equal('value'); $handle->methodA->calledWith('argument'); }); });
eloquent/phony-peridot包还提供了自动装配的模拟
use function Eloquent\Phony\on; describe('Phony for Peridot', function () { it('supports auto-wiring', function (ClassA $mock) { $handle = on($mock); $handle->methodA->with('argument')->returns('value'); expect($mock->methodA('argument'))->to->equal('value'); $handle->methodA->calledWith('argument'); }); });
PHPUnit使用方法
安装eloquent/phony-phpunit包,然后
use Eloquent\Phony\Phpunit\Phony; class PhonyTest extends PHPUnit_Framework_TestCase { public function testIntegration() { $handle = Phony::mock('ClassA'); $handle->methodA->with('argument')->returns('value'); $mock = $handle->get(); $this->assertSame('value', $mock->methodA('argument')); $handle->methodA->calledWith('argument'); } }
启动
如果这一部分有偏见,或者我对某个框架的某个细节回忆不正确,请见谅。但如果你想知道我创建Phony的原因的总结版,基本上
- Phake不支持特性,而且曾经不支持HHVM。
- PHPUnit和Mockery都实现了预期-运行-验证风格的模拟,这是非常糟糕的。
- Prophecy不支持我需要的功能。
- SimpleTest已经过时了。
- 我没有听说过其他听起来有希望的东西。
我最初使用的模拟框架可能是SimpleTest的。不幸的是,那已经是很久以前的事了,当时我对模拟并不真正理解。更糟糕的是,我甚至记不起SimpleTest的模拟是如何工作的。所以我们直接跳到我对第一个真正深入研究过的模拟框架,也就是PHPUnit的。
当我第一次发现PHPUnit的模拟时,它们对我来说是革命性的。它们允许我以我以前不知道是可能的方式彻底地测试事物。尽管PHPUnit的模拟可能是某些现有的Java模拟系统的移植,但它是我第一次睁开眼睛看到测试替身的真正潜力。
不幸的是,并非一切都如阳光明媚。使用PHPUnit的模拟很困难,特别是由于预期-运行-验证风格的接口,它们很难重用。无法在特定测试中“取消预期”某事物,当某事物失败时,真正的失败原因往往很难确定。
在寻找更好的解决方案的过程中,我偶然发现了Phake,它受到了Java非常流行的Mockito模拟框架的启发。Phake曾经是,现在仍然是出色的模拟框架。与Mockito和Phake不同,它们放弃了预期-运行-验证模式,带来了巨大的好处。
通过将存根和验证视为不同的概念,Phake基本上解决了我对PHPUnit模拟的所有问题。模拟可以轻松重用,当测试失败时,原因(通常)是清晰的。我彻底放弃了预期-运行-验证的邪恶,发誓永远不再回头。
我认为正是在这个时期,我听说了一个叫做Mockery的工具。尽管我对Phake相当满意,但它确实有几个小问题,比如它处理引用参数的方式,以及无法对traits进行模拟。所以我查看了Mockery,但立即被它使用期望的方式吓到了,这让我觉得这会是一个巨大的倒退。
公平地说,Mockery可能支持其他模拟方法,但由于它的“主要”工作方式似乎基于expect-run-verify,我从未将其视为可行的候选人,也从未使用过它。
在这个时期,我参与了一个Node.js项目,并探索了几个最受欢迎的Node模拟框架,最终选择了优秀的Sinon.JS。它专注于基于回调的存根和间谍,以及其广泛的验证API,最终对Phony产生了重大影响。
直到HHVM在PHP世界成为一个可行的选择,我才真正考虑使用除了Phake之外的其他工具。我一直在我的项目中想要开始尝试HHVM。不幸的是,Phake存在的问题甚至阻止了测试套件在HHVM下运行,因此这立即成为一个问题。
我的一个同事本来打算为Phake添加HHVM支持,但不幸的是,当时似乎Phake的工作已经停滞,仅仅接受了一个添加HHVM作为测试目标的PR就花了一个多月。值得赞扬的是,Phake现在支持HHVM,事后看来,HHVM支持是非常困难的。
一个有前途的项目是Prophecy,这是一个从phpspec测试框架中出现的“有偏见”的模拟框架。虽然我不喜欢它使用“预言”和“揭示”等抽象术语作为方法名,但使用单独的对象实例来控制实际模拟的核心想法非常有效。实际上非常有效,以至于我最终在Phony中使用了这个概念。
重要的是,Prophecy 已经支持 HHVM,看起来非常适合取代 Phake;直到我遇到了 Prophecy 本质上的“固执”一面。 Prophecy 不支持的一件事是顺序验证。例如;验证代码在写入文件之前是否打开了文件。在我看来,这是一个好处显而易见的特性,但遗憾的是,Prophecy 的开发者 并不认同。
因此,愚蠢的我以为“这能有多难?”于是开始着手开发 Phony,这是一个旨在结合其前辈优势的模拟框架,并允许在 HHVM 下进行测试,而不做任何妥协。
随着新版本的 PHP 不断推出并引入新的语言特性,Phony 也适应了测试这些特性的需求。我也非常幸运,能在我的日常工作中的开发团队中成为 Phony 的测试床,这极大地促进了 Phony 的稳定性和最终功能集。
当然,这比最初的预期要漫长得多,Phony 仍然是一个具有挑战性的维护项目。但对我来说,它是一个几乎每天都会使用的宝贵工具,我希望它也能对你们有所帮助。
感谢聆听。
Erin (@ezzatron)
感谢
特别感谢以下人员
- @jmalloc,@koden-km,@parkertr 和 @darianbr,他们作为测试对象给予了宝贵的帮助。
- @szczepiq 和为 Mockito 做出贡献的所有人。
- @mlively 和为 Phake 做出贡献的所有人。
- @cjohansen 和为 Sinon.JS 做出贡献的所有人。
- @everzet 和为 Prophecy 做出贡献的所有人。
- @sebastianbergmann 和为 PHPUnit 做出贡献的所有人。
- @jails 和为 Kahlan 做出贡献的所有人。
许可证
有关完整的版权和许可信息,请参阅 LICENSE 文件。