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

如何测试Ships类addShip方法?能否Mock内部实例化类?

如何测试Ships类addShip方法中内部实例化的依赖类

先看你提供的addShip方法代码:

public function addShip($data, $columns = [])
{
    $columns = $columns ? $columns : $this->defaultColumns;
    $bridgeOfficers = new BridgeOfficers(['pdo' => $this->pdo]);
    $abilities = new Abilities(['pdo' => $this->pdo]);
    $traits = new Traits(['pdo' => $this->pdo]);
    $prices = new Prices(['pdo' => $this->pdo]);

    //...
}

能不能直接Mock这些内部实例化的类?

直接Mock方法内部new出来的BridgeOfficers、Abilities等类基本不可行:

  • 主流PHP测试框架(比如PHPUnit、Mockery)无法直接拦截硬编码的new操作,除非用字节码篡改这类复杂的黑魔法手段。
  • 就算勉强实现,测试代码会变得极其复杂、脆弱,后续代码改动很容易导致测试失效,维护成本极高。

最优方案:重构代码提升可测试性

这种硬编码依赖实例的写法违反了依赖倒置原则,导致Ships类和这些依赖类耦合度太高。通过依赖注入重构后,不仅测试会变得简单,代码的扩展性和维护性也会大幅提升。

方案1:构造函数注入(适合单实例复用场景)

把依赖类的实例通过构造函数传入Ships类,测试时直接传入Mock对象即可:

class Ships {
    private $bridgeOfficers;
    private $abilities;
    private $traits;
    private $prices;
    private $defaultColumns;
    private $pdo;

    // 构造函数注入所有依赖
    public function __construct(
        BridgeOfficers $bridgeOfficers,
        Abilities $abilities,
        Traits $traits,
        Prices $prices,
        $pdo,
        $defaultColumns
    ) {
        $this->bridgeOfficers = $bridgeOfficers;
        $this->abilities = $abilities;
        $this->traits = $traits;
        $this->prices = $prices;
        $this->pdo = $pdo;
        $this->defaultColumns = $defaultColumns;
    }

    public function addShip($data, $columns = [])
    {
        $columns = $columns ?: $this->defaultColumns;
        // 直接使用已注入的实例,无需再new
        // $this->bridgeOfficers->xxx();
        //...
    }
}

测试代码示例:

// 创建各个依赖的Mock对象
$mockBridgeOfficers = $this->createMock(BridgeOfficers::class);
$mockAbilities = $this->createMock(Abilities::class);
$mockTraits = $this->createMock(Traits::class);
$mockPrices = $this->createMock(Prices::class);

// 初始化Ships实例,传入Mock
$ships = new Ships(
    $mockBridgeOfficers,
    $mockAbilities,
    $mockTraits,
    $mockPrices,
    $yourPdoInstance,
    $defaultColumns
);

// 执行测试逻辑
$ships->addShip($testData);

方案2:工厂方法注入(适合每次调用都需要新实例的场景)

如果addShip每次调用都需要创建新的依赖实例,可以通过注入工厂闭包来实现:

class Ships {
    private $bridgeOfficersFactory;
    private $abilitiesFactory;
    private $traitsFactory;
    private $pricesFactory;
    private $defaultColumns;
    private $pdo;

    public function __construct(
        callable $bridgeOfficersFactory,
        callable $abilitiesFactory,
        callable $traitsFactory,
        callable $pricesFactory,
        $pdo,
        $defaultColumns
    ) {
        $this->bridgeOfficersFactory = $bridgeOfficersFactory;
        $this->abilitiesFactory = $abilitiesFactory;
        $this->traitsFactory = $traitsFactory;
        $this->pricesFactory = $pricesFactory;
        $this->pdo = $pdo;
        $this->defaultColumns = $defaultColumns;
    }

    public function addShip($data, $columns = [])
    {
        $columns = $columns ?: $this->defaultColumns;
        // 通过工厂创建实例
        $bridgeOfficers = ($this->bridgeOfficersFactory)();
        $abilities = ($this->abilitiesFactory)();
        $traits = ($this->traitsFactory)();
        $prices = ($this->pricesFactory)();

        //...
    }
}

测试代码示例:

// 创建Mock对象
$mockBridgeOfficers = $this->createMock(BridgeOfficers::class);
// 定义工厂闭包,返回Mock
$bridgeOfficersFactory = fn() => $mockBridgeOfficers;

// 其他依赖同理处理
$mockAbilities = $this->createMock(Abilities::class);
$abilitiesFactory = fn() => $mockAbilities;

// 初始化Ships实例
$ships = new Ships(
    $bridgeOfficersFactory,
    $abilitiesFactory,
    $traitsFactory,
    $pricesFactory,
    $yourPdoInstance,
    $defaultColumns
);

// 执行测试
$ships->addShip($testData);

总结

硬编码new依赖的写法会让代码变得难以测试和维护,重构为依赖注入是最合理的选择。这种改动不仅能解决当前的测试问题,还能让代码结构更清晰,后续扩展新功能也更方便。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 19:57:48