eloquent/phony

此包已被废弃,不再维护。没有建议的替代包。

PHP 的模拟、存根和间谍工具。


README

不再维护

此包不再维护。有关更多信息,请参阅此声明

Phony

PHP 的模拟、存根和间谍工具。

Current version image

Example verification output

安装

作为各种 Composer 包提供,具体取决于使用的测试框架

请参阅与测试框架集成部分的文档

文档

完整的文档可供查阅。

Phony 是什么?

Phony 是一个用于创建各种类型的测试替身的 PHP 库,包括对象 模拟、函数 存根和函数 间谍

为什么使用 Phony

对旧版和新版语言特性的支持

Phony 承诺支持新出现的 PHP 功能。需要在其他模拟框架中采取变通方法的特性(如按引用传递的参数),通常在 Phony 中“正常工作”。

Phony 支持以下特性:

零配置集成

Phony 集成到您喜欢的测试框架中就像 选择正确的命名空间导入 一样简单。互补功能使集成无缝且直观

对与其他测试框架的更好集成感兴趣?我们也是!如果有什么我们可以做的,请打开一个 GitHub 问题

精炼的验证输出

Phony 在其 验证输出 方面进行了大量的工作和完善

Example verification output

Phony 的输出包括简洁但详细的信息,旨在帮助您尽快找到失败的原因。在适当的情况下,输出使用 差异比较和颜色 来进一步突出重要细节。

函数和回调的一等支持

Phony 设计用于支持测试基于对象和基于函数的系统。在 Phony 中,对象模拟建立在功能级全功能的 存根间谍 之上。

这种对支持过程式编程的关注使 Phony 能够处理仅基于类模拟框架无法处理的许多情况。由于 Phony 的类级支持基于其功能级支持,接口是一致的,并且使用时不需要额外了解很多知识。

广泛的功能集

Phony 拥有广泛而强大的功能集。有关特定功能的详细信息,请选择以下选项之一

帮助

对于困难的测试场景的帮助、有关如何使用Phony的问题或报告Phony本身的问题,请打开GitHub问题,以便其他人也能从中受益。

或者,可以直接通过Twitter联系@ezzatron

用法

有关详细用法,请参阅文档

示例测试套件

请参阅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的原因的总结版,基本上

我最初使用的模拟框架可能是SimpleTest的。不幸的是,那已经是很久以前的事了,当时我对模拟并不真正理解。更糟糕的是,我甚至记不起SimpleTest的模拟是如何工作的。所以我们直接跳到我对第一个真正深入研究过的模拟框架,也就是PHPUnit的。

当我第一次发现PHPUnit的模拟时,它们对我来说是革命性的。它们允许我以我以前不知道是可能的方式彻底地测试事物。尽管PHPUnit的模拟可能是某些现有的Java模拟系统的移植,但它是我第一次睁开眼睛看到测试替身的真正潜力。

不幸的是,并非一切都如阳光明媚。使用PHPUnit的模拟很困难,特别是由于预期-运行-验证风格的接口,它们很难重用。无法在特定测试中“取消预期”某事物,当某事物失败时,真正的失败原因往往很难确定。

在寻找更好的解决方案的过程中,我偶然发现了Phake,它受到了Java非常流行的Mockito模拟框架的启发。Phake曾经是,现在仍然是出色的模拟框架。与MockitoPhake不同,它们放弃了预期-运行-验证模式,带来了巨大的好处。

通过将存根和验证视为不同的概念,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)

感谢

特别感谢以下人员

许可证

有关完整的版权和许可信息,请参阅 LICENSE 文件