如何测试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
相关产品推荐
相关产品推荐

