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

PHP Mockery 静态方法Mock失效求解:如何正确实现Mock

问题分析

你遇到的Mock不生效的核心原因是:MyApp\Api\Ethereum::getBalances里直接调用了静态方法Eth::validateAddress(),而你当前的Mock方式只针对实例方法,无法拦截这种硬编码的静态调用。下面给你两种解决方案,优先推荐更符合可测试性的重构方案。


解决方案1:重构代码使用依赖注入(推荐)

静态调用的最大问题就是难以测试和扩展,通过依赖注入把Ethereum服务实例化传入API类,能从根本上解决这个问题。

修改API类(MyApp\Api\Ethereum)

把静态调用替换为实例方法调用,通过构造函数注入服务:

<?php 
namespace MyApp\Api; 

use MyApp\ApiBase; 
use MyApp\Ethereum as Eth; 

/** 
 * Class Ethereum 
 * @package MyApp\Api 
 */ 
class Ethereum extends ApiBase { 
    private $ethereumService;

    // 构造函数注入Ethereum服务实例
    public function __construct(Eth $ethereumService) {
        $this->ethereumService = $ethereumService;
    }

    /** 
     * Function to get balances 
     * @param array $addresses 
     * @param string $tag 
     * @return array 
     */ 
    public function getBalances(array $addresses, string $tag = 'latest') { 
        $data = []; 
        foreach ($addresses as $addr) { 
            // 调用实例方法而非静态方法
            if (!$this->ethereumService->validateAddress($addr)) { 
                continue; 
            }
            // 这里可以补充后续获取余额的逻辑,比如:
            // $data[$addr] = $this->fetchBalance($addr, $tag);
        } 
        return $data; 
    } 
}

修改测试类

现在可以轻松MockEth的实例并注入到API类中:

<?php 
declare(strict_types=1); 
namespace MyApp\Test; 

use MyApp\Api\Ethereum; 
use MyApp\Ethereum as Eth; 
use Mockery\Adapter\Phpunit\MockeryTestCase; 

/** 
 * @covers MyApp\Api\Ethereum 
 */ 
final class EthereumTest extends MockeryTestCase { 
    protected $ethereumApi; 
    protected $ethereum_address; 

    protected function setUp(): void { 
        $this->ethereum_address = '0x0000000000000000000000000000000' . rand(100000000, 999999999);
        
        // Mock Eth类的实例
        $mockEthereumService = \Mockery::mock(Eth::class);
        $mockEthereumService->shouldReceive('validateAddress')
            ->once()
            ->with($this->ethereum_address)
            ->andReturn(true);
        
        // 将Mock实例注入API类
        $this->ethereumApi = new Ethereum($mockEthereumService);
    } 

    public function testGetBalances_ValidEthereumAddress(): void { 
        $addresses = [$this->ethereum_address]; 
        $result = $this->ethereumApi->getBalances($addresses); 
        // 注意:原代码的getBalances目前返回空数组,若后续添加了余额逻辑,再改为assertNotEmpty
        $this->assertEmpty($result); 
    } 

    public function tearDown(): void { 
        \Mockery::close(); 
    } 
}

解决方案2:Mock静态方法(临时快速解决)

如果暂时不想重构代码,可以用Mockery的静态Mock功能拦截静态调用,但这种方式可能导致测试间的污染,不推荐长期使用:

public function testGetBalances_ValidEthereumAddress(): void {
    // 静态Mock Eth类的validateAddress方法
    \Mockery::mock('static:' . Eth::class)
        ->shouldReceive('validateAddress')
        ->once()
        ->with($this->ethereum_address)
        ->andReturn(true);

    $addresses = [$this->ethereum_address]; 
    $result = $this->ethereumApi->getBalances($addresses); 
    $this->assertEmpty($result); 
}

额外提示

原代码的getBalances方法目前仅过滤无效地址,但最终返回空数组,所以你的assertNotEmpty断言会失败,需要根据实际业务逻辑调整断言或补充余额获取代码。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:13:05