bizkit/phpunit-function-mock 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

bizkit/phpunit-function-mock

Composer 安装命令:

composer require --dev bizkit/phpunit-function-mock

包简介

Provides a small PHPUnit extension for mocking native PHP functions from tests.

README 文档

README

Latest Stable Version Build Status Code Coverage License

Provides a small PHPUnit extension for mocking native PHP functions from tests.

The idea is based on Symfony PHPUnit Bridge's ClockMock and DnsMock: register a function in the tested namespace and dispatch that function through a test-controlled mock. See Symfony's Clock Mocking and DNS-sensitive tests documentation for the original pattern.

Installation

composer require --dev bizkit/phpunit-function-mock

PHPUnit Configuration

Register the extension in your PHPUnit configuration:

<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="vendor/autoload.php">
    <extensions>
        <bootstrap class="Bizkit\FunctionMock\PHPUnitExtension" />
    </extensions>
</phpunit>

Usage

Annotate the test class or method with MockFunction, then configure the callable in the test body with FunctionMock.

<?php

declare(strict_types=1);

namespace App\Tests\Service;

use App\Service\TokenGenerator;
use Bizkit\FunctionMock\Attribute\MockFunction;
use Bizkit\FunctionMock\FunctionMock;
use PHPUnit\Framework\TestCase;

#[MockFunction('random_int')]
final class TokenGeneratorTest extends TestCase
{
    public function testGenerateToken(): void
    {
        FunctionMock::mock('random_int', static fn (int $min, int $max): int => 1234);

        self::assertSame('token-1234', new TokenGenerator()->generate());
    }
}

When the test suite is loaded, the extension registers a random_int() function in the test namespace and in the matching application namespace with Tests removed. For example, App\Tests\Service\TokenGeneratorTest also registers mocks for App\Service.

Multiple Functions

Pass a list when a test needs several functions:

#[MockFunction(['file_exists', 'is_file'])]
final class FileLoaderTest extends TestCase
{
    public function testLoad(): void
    {
        FunctionMock::mockMany([
            'file_exists' => static fn (string $path): bool => true,
            'is_file' => static fn (string $path): bool => true,
        ]);

        // ...
    }
}

MockFunction is repeatable, so class-level and method-level attributes can be combined.

Class Override

Use class: when the tested code lives in a namespace that cannot be inferred from the test class name.

use App\Service\TokenGenerator;
use Bizkit\FunctionMock\Attribute\MockFunction;

#[MockFunction('random_int', class: TokenGenerator::class)]
final class TokenGeneratorTest extends TestCase
{
}

The function is registered in the namespace of the class passed to class:.

Manual Registration

You can also register functions without PHPUnit attributes:

use App\Service\TokenGenerator;
use Bizkit\FunctionMock\FunctionMock;

FunctionMock::register(TokenGenerator::class, 'random_int');
FunctionMock::mock('random_int', static fn (int $min, int $max): int => 1234);

Cleanup

Mocks are global process state. The PHPUnit extension clears configured callables after annotated tests finish, error, or skip. If you configure mocks manually in unannotated tests, clear them yourself:

protected function tearDown(): void
{
    FunctionMock::reset();
}

Known Limitations

This library relies on PHP's fallback to the global namespace for functions. In namespaced code, an unqualified function call such as random_int() first checks for a function in the current namespace, then falls back to \random_int().

Because of that, mocks only work for unqualified function calls:

namespace App\Service;

random_int(1, 10); // can be mocked
\random_int(1, 10); // cannot be mocked by this library

The namespaced function must also be registered before the target namespace calls that function for the first time. PHP can cache the first function resolution in its literal cache, so if App\Service\random_int() does not exist yet and PHP resolves the first call to \random_int(), later defining App\Service\random_int() may not affect that already-compiled call site. See PHP bug #64346. This is why the attribute-based extension registers functions when PHPUnit loads the test suite, before test methods execute.

Other limitations:

  • Generated namespaced functions cannot be removed once declared. Only their configured callables are reset.
  • Tests that register the same namespaced functions should run in separate processes when isolation matters.

Versioning

This project follows Semantic Versioning 2.0.0.

Reporting Issues

Use the project's issue tracker to report bugs or request improvements.

License

See the LICENSE file for details (MIT).

统计信息

  • 总下载量: 0
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 5
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 1
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-06-28