You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在PHPUnit测试中跳过内部私有调用并访问私有属性?

问题解决方案

错误原因

你尝试mock私有方法pending()导致报错,核心原因是PHPUnit的MockBuilder仅能配置公共方法,私有方法属于类内部实现细节,无法直接被mock或配置。

解决方案

方案1:修改原代码(推荐,符合可测试性设计)

将Receipt类中的pending()方法从private改为protected(私有方法是类内部实现细节,若公共方法依赖其逻辑,改为protected更利于测试和扩展):

// Receipt.php
namespace TDD;
class Receipt {
    // ... 其他代码不变 ...
    protected function pending()
    {
        $sql = 'select pending_amount from Pending_transtions where user_id =' . $this->user_id . ' limit 1;';
        //$pending_amt = $this->mainDb->get_sql_row($sql);
        //$this->pending = $pending_amt['pending_amount'];  
        $this->pending_amount = 55;
    }
    // ... 其他代码不变 ...
}

然后编写测试方法:

// ReceiptTest.php
public function testTotalWithoutPending()
{
    // 创建部分Mock,仅mock protected的pending()方法
    $receipt = $this->getMockBuilder(Receipt::class)
        ->setConstructorArgs(['s', 'p'])
        ->onlyMethods(['pending']) // PHPUnit 9+使用onlyMethods,旧版本用setMethods
        ->getMock();

    // 让mock的pending()方法不执行任何逻辑
    $receipt->method('pending')
        ->willReturn(null);

    // 访问并设置私有属性pending_amount的测试值
    $pendingProp = new \ReflectionProperty(Receipt::class, 'pending_amount');
    $pendingProp->setAccessible(true);
    $pendingProp->setValue($receipt, 45);

    $input = [0, 2, 5, 8];
    $expectedTotal = array_sum($input) + 45; // 计算预期结果:15+45=60
    $output = $receipt->total($input);

    $this->assertEquals($expectedTotal, $output);
}

方案2:不修改原代码(纯Reflection实现)

若无法修改原代码,可通过匿名子类重写total()方法,跳过pending()调用,同时用Reflection访问私有属性:

// ReceiptTest.php
public function testTotalWithoutPending()
{
    $input = [0, 2, 5, 8];
    $expectedPendingAmount = 45;
    $expectedTotal = array_sum($input) + $expectedPendingAmount;

    // 创建匿名子类,重写total()方法跳过pending()调用
    $receipt = new class extends Receipt {
        public function total(array $items = []) {
            // 用Reflection访问父类私有属性pending_amount
            $prop = new \ReflectionProperty(parent::class, 'pending_amount');
            $prop->setAccessible(true);
            $items[] = $prop->getValue($this);
            return array_sum($items);
        }
    };

    // 设置私有属性pending_amount的测试值
    $pendingProp = new \ReflectionProperty(Receipt::class, 'pending_amount');
    $pendingProp->setAccessible(true);
    $pendingProp->setValue($receipt, $expectedPendingAmount);

    $output = $receipt->total($input);
    $this->assertEquals($expectedTotal, $output);
}

关于访问私有属性pending_amount

无论哪种方案,都可通过ReflectionProperty实现对私有属性的读写:

// 获取私有属性并设置为可访问
$prop = new \ReflectionProperty(Receipt::class, 'pending_amount');
$prop->setAccessible(true);
// 设置属性值
$prop->setValue($receipt, 45);
// 获取属性值
$currentValue = $prop->getValue($receipt);

内容的提问来源于stack exchange,提问作者Siddhartha

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 22:09:15