idimsh/php-internals-mocker

帮助模拟PHP内部函数调用

v1.0.0 2019-12-03 21:33 UTC

This package is auto-updated.

Last update: 2024-09-22 06:32:15 UTC


README

Latest Version on Packagist Software License Build Status Coverage Status Quality Score Total Downloads PHP Version

实用工具,允许在测试中模拟PHP内部函数调用。

安装

推荐的安装方法是使用Composer。运行以下命令安装最新版本的包并将其添加到项目的composer.json

composer require-dev idimsh/php-internals-mocker

使用方法

此模拟器旨在用于单元测试,假设一个类似以下的类

namespace Vendor\Namespace

class MyClass 
{
    public function openConnction($hostname) 
    {
        return fsockopen($hostname);
    }
}

需要针对openConnction()方法进行单元测试。一个PhpUnit测试用例可能是这样的

namespace VendorTest\Namespace

class MyClassTest extends \PHPUnit\Framework\TestCase
{
    public function testOpenConnction(): void
    {
        $object   = new \Vendor\Namespace\MyClass;
        $hostname = \uniqid('hostname');
        $actual   = $object->openConnection($hostname);
        // ...
    }
}

我们确实不想在单元测试中打开连接,因此这个模拟器可以避免调用原生的PHP fsockopen(),并将其替换为对定义的回调的调用

namespace VendorTest\Namespace

use idimsh\PhpInternalsMocker\PhpFunctionSimpleMocker;

class MyClassTest extends \PHPUnit\Framework\TestCase
{
    protected function setUp(): void
    {
        parent::setUp();
        PhpFunctionSimpleMocker::reset();
    }
    
    public function testOpenConnction(): void
    {
        $hostname = \uniqid('hostname');
        $return   = \uniqid('some mock for the return of fsockopen()');
        
        PhpFunctionSimpleMocker::add(
            'fsockopen',
            \Vendor\Namespace\MyClass::class,
            function ($inputHostname) use ($hostname, $return) {
                static::assertSame($inputHostname, $hostname);
                return $return;
            }
        );
        
        $object = new \Vendor\Namespace\MyClass;
        $actual = $object->openConnection($hostname);
        static::assertSame($return, $actual);

        /** @noinspection PhpUnhandledExceptionInspection */
        PhpFunctionSimpleMocker::phpUnitAssertNotEnoughCalls($this);
    }
}

方法手册

1- PhpFunctionSimpleMocker::reset():应在PhpUnit TestCase 的 setUp() 方法或测试方法开始时调用。

2- PhpFunctionSimpleMocker::add():在调用 reset() 之后调用,以注册预期调用原生函数的回调,签名

    /**
     * Register a call back to be called for the PHP internal function which is to be used in the class passed.
     *
     * If the $callback is null, then this PHP function is not expected to be called.
     *
     * Assertions can be done inside the callback.
     *
     * @param string        $internalFunctionName The PHP function name to mock
     * @param string        $beingCalledFromClass The class FQN which calls $internalFunctionName
     * @param callable|null $callback
     * @param int           $numberOfCalls        To mock more than once for the same callback, pass the number here
     */
    public static function add(
        string $internalFunctionName,
        string $beingCalledFromClass,
        ?callable $callback,
        int $numberOfCalls = 1
    ): void

它可以多次使用相同的 $internalFunctionName 和不同的 $callback 调用,顺序与预期相同。
$beingCalledFromClass 预期一个类的FQN,从这个命名空间中提取出来,并将函数注册在该命名空间。

3- PhpFunctionSimpleMocker::phpUnitAssertNotEnoughCalls($testCase):在PhpUnit测试方法中所有断言注册完毕后(最后一行)调用,此方法将确保达到最小调用次数。

4- PhpFunctionSimpleMocker::assertPostConditions(?$testCase):是 PhpFunctionSimpleMocker::phpUnitAssertNotEnoughCalls($testCase) 的替代方法,并应从 PhpUnit TestCase 方法中调用:assertPostConditions(),而不是在每个测试方法结束时调用前一个方法,只需要一个调用传递 TestCase 就足够断言最小计数。

使用条件

要模拟并替换为回调的原生PHP函数调用需要(所有都必须适用)

  • 从类方法或定义在命名空间内的函数中调用,而不是从全局命名空间中的类方法或函数中调用。
  • PHP原生函数的调用之前不能有全局命名空间解析运算符 '\'
  • 在类中不使用 use function 语句导入该原生函数到命名空间。

限制

快速

  • 目前不支持使用引用的PHP原生函数,但计划支持。
  • 在PhpUnit中,必须显式处理调用次数不足的断言,通过调用 PhpFunctionSimpleMocker::phpUnitAssertNotEnoughCalls($this)PhpFunctionSimpleMocker::assertPostConditions($this),如果有更好的想法,请分享。
  • 对于任何奇怪的问题,PhpUnit的 @runInSeparateProcess 选项可能有所帮助,尽管我尚未遇到此类情况,请报告任何问题。

致谢

替代方案

有一个我尚未测试的解决方案 php-mock

许可证

在MIT许可证下发布 - 详细内容请参阅许可证文件