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

如何在PHPUnit中Mock Magento的Mage::getSingleton('checkout/cart')?

解决Magento中无法Mock Mage::getSingleton('checkout/cart')的单元测试问题

问题描述

在为遗留Magento代码的foo()方法编写单元测试时,尝试Mock全局静态方法Mage::getSingleton('checkout/cart')失败,执行测试时触发致命错误,且无法修改源方法代码。

源方法代码

public function foo()
{
    return Mage::getSingleton('checkout/cart')->getQuote()->getFunctionName();
}

测试函数代码

public function testFoo(): void
{
    //$this->className :: Project_Catalog_Helper_Test
    $expected = 'string';
    $this->className->method('getSingleton')->willReturn($this->cart);
    $this->cart->method('getQuote')->willReturn($this->cart);
    $this->cart->method('getFunctionName')->willReturn('string');
    $this->assertEquals(
        $this->className->foo(),
        $expected
    );
}

报错日志

MacBook-Pro % ./vendor/bin/phpunit tests/src/app/code/local/project/Catalog/Helper/OrderTest.php
PHPUnit 6.5.14 by Sebastian Bergmann and contributors.

Runtime:       PHP 7.3.11
Configuration: /project_path/phpunit.xml.dist

E                                                                   1 / 1 (100%)

Time: 150 ms, Memory: 8.00MB

There was 1 error:

1) Project_Catalog_Helper_Test::testFoo
Error: Call to a member function getCode() on bool

/project_path/app/code/core/Mage/Customer/Model/Session.php:103
/project_path/app/code/core/Mage/Core/Model/Config.php:1394
/project_path/app/Mage.php:517
/project_path/app/Mage.php:531
/project_path/app/code/community/Checkout/Model/Cart.php:20
/project_path/app/code/local/project/Catalog/Helper/Order.php:21
/project_path/tests/src/app/code/local/project/Catalog/Helper/OrderTest.php:100
/project_path/vendor/phpunit/phpunit/phpunit:53

问题原因

你当前的Mock逻辑完全错误:你Mock的是测试类实例$this->className的getSingleton方法,但源方法中调用的是全局静态类Mage的getSingleton方法,二者没有关联。Mock无效导致Mage实例化了真实的Checkout_Model_Cart类,而该类初始化时依赖Mage_Customer_Model_Session,测试环境中Session初始化失败返回布尔值,进而触发Call to a member function getCode() on bool错误。

解决办法

以下两种方案均无需修改源方法代码,适配遗留代码场景:

方案1:使用PHPUnit静态Mock(适配PHPUnit 6.x)

通过PHPUnit的MockStatic直接MockMage类的静态方法:

public function testFoo(): void
{
    $expected = 'string';

    // 1. 创建Quote对象Mock,模拟目标方法返回值
    $quoteMock = $this->createMock(Mage_Sales_Model_Quote::class);
    $quoteMock->method('getFunctionName')->willReturn($expected);

    // 2. 创建Cart对象Mock,模拟getQuote返回上述Quote Mock
    $cartMock = $this->createMock(Mage_Checkout_Model_Cart::class);
    $cartMock->method('getQuote')->willReturn($quoteMock);

    // 3. Mock Mage类的静态getSingleton方法
    $mageStaticMock = $this->getMockBuilder(Mage::class)
        ->setMethods(['getSingleton'])
        ->getMockStatic();
    $mageStaticMock::method('getSingleton')
        ->with('checkout/cart')
        ->willReturn($cartMock);

    // 执行断言
    $this->assertEquals($this->className->foo(), $expected);
}

方案2:使用反射强制替换Mage单例存储

若静态Mock兼容性不佳,可通过反射直接修改Mage类的单例缓存:

public function testFoo(): void
{
    $expected = 'string';

    // 1. 创建依赖的Mock对象
    $quoteMock = $this->createMock(Mage_Sales_Model_Quote::class);
    $quoteMock->method('getFunctionName')->willReturn($expected);

    $cartMock = $this->createMock(Mage_Checkout_Model_Cart::class);
    $cartMock->method('getQuote')->willReturn($quoteMock);

    // 2. 用反射获取并修改Mage的$_singletons静态属性
    $mageReflection = new ReflectionClass(Mage::class);
    $singletonsProp = $mageReflection->getProperty('_singletons');
    $singletonsProp->setAccessible(true);

    // 将checkout/cart的单例替换为我们的Mock
    $originalSingletons = $singletonsProp->getValue();
    $singletonsProp->setValue(array_merge($originalSingletons, [
        'checkout/cart' => $cartMock
    ]));

    // 3. 执行断言
    $this->assertEquals($this->className->foo(), $expected);

    // 4. 清理:恢复原始单例数据,避免影响其他测试
    $singletonsProp->setValue($originalSingletons);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 08:25:16