PHP 的模拟、存根和间谍

5.0.3 2023-09-03 23:12 UTC

This package is auto-updated.

Last update: 2024-09-04 01:19:25 UTC


README

Phony

PHP 的模拟、存根和间谍

Current version image

Example verification output

安装

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

请参阅 Integration with test frameworks 中的章节,了解如何在 文档 中集成测试框架。

文档

完整的 文档 可用。

Phony 是什么?

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

为什么使用 Phony

支持语言特性,新旧兼修

Phony 致力于支持新出现的 PHP 功能。在其他的模拟框架中需要工作区段(如按引用传递的参数)的功能,通常在 Phony 中“直接”工作。

Phony 支持以下功能:

零配置集成

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

想要与其他测试框架有更好的集成?我们也是!如果有我们可以做的事情,请在GitHub上提交一个问题

精细化的验证输出

Phony在验证输出方面做了大量工作,使实际操作更加精细;在验证输出方面。

Example verification output

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

对函数和回调的第一级支持

Phony旨在为基于对象和基于函数的系统提供支持。在Phony中,对象模拟建立在功能齐全的函数级别存根间谍之上。

这种对过程式编程支持的关注使得Phony可以处理许多仅由基于类的模拟框架无法处理的场景。由于Phony的类级别支持基于其函数级别支持,因此接口是一致的,并且使用时几乎不需要额外的知识。

广泛的功能集

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

帮助

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

或者,可以直接通过 @ezzatronTwitter 上联系。

用法

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

示例测试套件

请参阅 phony-examples 存储库。

独立使用

安装 [BridgeTroll-IO/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的模拟很困难,尤其是由于expect-run-verify风格的接口,它们难以再次使用。在特定测试中无法“取消预期”某件事,当某件事失败时,真正的失败原因通常很难确定。

在寻找更好的解决方案时,我偶然发现了Phake,它受到了Java非常流行的Mockito模拟框架的启发。Phake曾经是,并且仍然是出色的模拟框架。两者都放弃了expect-run-verify模式,带来了巨大的好处。

通过将存根和验证视为不同的概念,Phake本质上解决了我在PHPUnit模拟中遇到的所有问题。模拟可以轻松重复使用,当测试失败时,原因通常是明确的。我对expect-run-verify的恶行深信不疑,并发誓永远不再回头。

我认为大约在这个时候,我听说了Mockery。虽然我对Phake相当满意,但它确实有一些小怪癖,比如它处理引用参数的方式,以及无法模拟traits。所以我查看了Mockery,但很快就被它使用预期感到沮丧;我觉得这将是一个巨大的倒退。

公平地说,可能Mockery支持其他模拟方法,但由于它似乎主要基于expect-run-verify,我从未考虑过它是一个可行的候选者,也从未使用过它。

在大约这个时间,我参与了一个Node.js项目,探索了几个最流行的Node模拟框架,然后选择了出色的Sinon.JS。它关注基于回调的存根和监视,以及广泛的验证API,最终对Phony产生了重大影响。

直到HHVM成为PHP世界中的一个可行选项,我才真正开始考虑使用除了Phake之外的某些东西。我一直想在我的项目中开始尝试使用HHVM。不幸的是,由于Phake存在一些问题,阻止了测试套件在HHVM下运行,因此这立即成为了一个问题。

我的同事中有一位原本打算为HHVM支持Phake,但不幸的是,当时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文件