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

如何在PHPUnit测试中忽略私有pending()方法并访问pending_amount属性

代码文件

Receipt.php

<?php
namespace TDD;
class Receipt {
    private $user_id = 1;
    private $pending_amount;
    public function total(array $items = []){
        $items[] = $this->pending();
        return array_sum($items);
    }

    public function tax($amount,$tax){
        return $amount * $tax;
    }
    
    private 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'];  
        return $this->pending_amount = 45;
    }
    
    public function addTaxPending($tax){
        return $this->pending_amount * $tax;
    }
}

ReceiptTest.php

<?php
namespace TDD\Test;
require(dirname(dirname(__FILE__))).DIRECTORY_SEPARATOR.'vendor'.DIRECTORY_SEPARATOR.'autoload.php';
use PHPUnit\Framework\TestCase; 
use TDD\Receipt; 


class ReceiptTest extends TestCase{
    public function setUp(): void {
        $this->Receipt = new Receipt();
    }
    public function tearDown(): void{
        unset($this->Receipt);
    }
    public function testTotal(){
        $input = [0,2,5,8];
        $output = $this->Receipt->total($input);
        $this->assertEquals(15,$output,"this is not valid");        
    }   

    public function testTax(){
        $inputAmount = 10.00;
        $inputTax =  0.10;
        $output = $this->Receipt->tax($inputAmount,$inputTax);
        $this->assertEquals(1.0,$output,"this tax expecting 1.0");      
    }   
}
问题描述

由于pending()方法涉及数据库查询,我希望在PHPUnit测试中跳过total()对该私有方法的内部调用,同时能访问Receipt类的$this->pending_amount属性,且pending()必须保持私有。请问该怎么实现?


解决方案

可以通过PHPUnit模拟私有方法+PHP反射访问私有属性的组合来实现,完全不需要修改原类的代码结构:

<?php
namespace TDD\Test;
require(dirname(dirname(__FILE__))).DIRECTORY_SEPARATOR.'vendor'.DIRECTORY_SEPARATOR.'autoload.php';
use PHPUnit\Framework\TestCase; 
use TDD\Receipt; 
use ReflectionClass;

class ReceiptTest extends TestCase{
    private $receipt;

    public function setUp(): void {
        // 创建Receipt的部分模拟对象,仅替换pending方法
        $this->receipt = $this->getMockBuilder(Receipt::class)
            ->onlyMethods(['pending']) // 即使是私有方法也能指定模拟
            ->getMock();

        // 自定义pending方法的返回值,避免执行数据库逻辑
        $testPendingAmount = 0; // 这里可设置任意测试用值
        $this->receipt->method('pending')
            ->willReturn($testPendingAmount);

        // 用反射设置私有属性pending_amount,保证和模拟返回值一致
        $reflection = new ReflectionClass($this->receipt);
        $pendingProp = $reflection->getProperty('pending_amount');
        $pendingProp->setAccessible(true);
        $pendingProp->setValue($this->receipt, $testPendingAmount);
    }

    public function tearDown(): void{
        unset($this->receipt);
    }

    public function testTotal(){
        $input = [0,2,5,8];
        $output = $this->receipt->total($input);
        $this->assertEquals(15,$output,"计算结果不符合预期");        
    }   

    public function testTax(){
        $inputAmount = 10.00;
        $inputTax =  0.10;
        $output = $this->receipt->tax($inputAmount,$inputTax);
        $this->assertEquals(1.0,$output,"税费计算不符合预期");      
    }

    // 额外测试addTaxPending方法,同样可以自定义pending_amount的值
    public function testAddTaxPending(){
        $taxRate = 0.1;
        $customPendingAmount = 100;

        $reflection = new ReflectionClass($this->receipt);
        $pendingProp = $reflection->getProperty('pending_amount');
        $pendingProp->setAccessible(true);
        $pendingProp->setValue($this->receipt, $customPendingAmount);

        $output = $this->receipt->addTaxPending($taxRate);
        $this->assertEquals(10, $output, "待缴金额税费计算错误");
    }
}

核心逻辑说明

  1. 模拟私有方法:PHPUnit的getMockBuilder支持模拟私有方法,只要在onlyMethods中指定方法名即可,完全不会修改原类的访问修饰符。
  2. 访问私有属性:利用PHP的ReflectionClass可以临时将私有属性设置为可访问,直接修改其值,满足测试中对pending_amount的控制需求。
  3. 解耦数据库依赖:通过模拟pending()的返回值,彻底跳过数据库查询逻辑,让测试只关注业务计算逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 04:36:17