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
相关产品推荐
相关产品推荐

