如何在PHPUnit中模拟被测方法内创建的其他类对象
使用PHPUnit模拟依赖对象测试
sourceClass::myFunction() 你的问题太常见了——当代码里硬编码创建依赖对象时,测试逻辑完全被外部依赖牵着走,根本没法稳定验证自己的业务逻辑。解决这个问题的核心思路是让依赖可替换,再配合PHPUnit的Mock功能来控制依赖的行为,具体可以这么操作:
第一步:改造原类,支持依赖注入
首先得把sourceClass里直接new otherClass()的写法改掉,换成依赖注入(这里用构造函数注入为例,是最常用也最清晰的方式):
class sourceClass{ private $otherClass; // 通过构造函数传入otherClass实例,而非自行创建 public function __construct(otherClass $otherClass){ $this->otherClass = $otherClass; } function myFunction(){ // 现在使用注入的对象,不再硬编码创建 if($this->otherClass->someMethod() == 'want to controll it'){ return 'want this to get'; } } }
这么改的好处很明显:生产环境可以传入真实的otherClass实例,测试环境则能轻松替换成Mock对象,完全掌控依赖的行为。
第二步:编写带Mock的测试用例
接下来在PHPUnit测试中,创建otherClass的Mock对象,强制它的someMethod返回你预设的值,这样就能精准控制myFunction的执行路径:
class StubTest extends TestCase{ function testMyFunction(){ // 1. 创建otherClass的Mock实例 $mockOther = $this->createMock(otherClass::class); // 2. 指定规则:当调用someMethod时,返回我们想要的字符串 $mockOther->method('someMethod') ->willReturn('want to controll it'); // 3. 把Mock对象注入到sourceClass实例中 $source = new sourceClass($mockOther); // 4. 断言结果符合预期 $this->assertEquals('want this to get', $source->myFunction()); } }
特殊情况:没法修改原类怎么办?
如果因为某些限制不能改动sourceClass的代码(比如是第三方类库),可以用一个偏hack的应急方案——利用PHP反射替换类内的私有属性,但这种方式破坏了类的封装性,只建议临时使用:
function testMyFunctionWithoutModifyingSource(){ $source = new sourceClass(); // 创建Mock对象 $mockOther = $this->createMock(otherClass::class); $mockOther->method('someMethod')->willReturn('want to controll it'); // 用反射获取sourceClass里的$other_object属性 $reflection = new ReflectionClass($source); $property = $reflection->getProperty('other_object'); $property->setAccessible(true); // 把Mock对象赋值进去 $property->setValue($source, $mockOther); // 执行断言 $this->assertEquals('want this to get', $source->myFunction()); }
当然,还是优先推荐改造原类实现依赖注入,这是更健壮、可维护的代码设计思路。
内容的提问来源于stack exchange,提问作者Prze Gee
相关产品推荐
相关产品推荐

