Laravel中如何测试调用同类静态方法的静态方法?
解决Laravel中测试调用同类静态方法的静态方法问题
问题根源
你遇到的报错有两个核心原因:
- 使用
alias:或overload:创建的Mock会完全替换原类,但你只Mock了bar方法,未定义foo方法,调用Foo::foo()时自然会提示方法不存在。 - 原代码中
foo方法使用self::bar()调用,self是静态绑定到Foo类本身,即使创建了Mock子类,self仍会调用原类的bar方法,而非Mock的实现。
可行解决方案
方案一:修改原代码(推荐,更符合面向对象设计)
将foo方法中的self::bar()改为static::bar(),利用PHP的延迟静态绑定,让调用指向当前运行时的类(即Mock子类):
class Foo { public static function foo() { return !static::bar(); // 替换self为static } public static function bar() { $trueOrFalse = [true, false]; $key = array_rand($trueOrFalse); return $trueOrFalse[$key]; } }
对应的测试代码:
class FooClassTest extends TestCase { public function testFooMethod() { // 创建重载Mock,保留原类的foo方法实现 $mock = Mockery::mock('overload:' . Foo::class)->makePartial(); // 指定bar方法返回固定值true $mock->shouldReceive('bar')->once()->andReturn(true); // 断言foo返回bar的相反值 $this->assertEquals(false, Foo::foo()); // 清理Mockery Mockery::close(); } }
方案二:不修改原代码,用反射替换方法实现
如果无法修改原代码,可通过PHP反射强制重写bar方法的逻辑:
class FooClassTest extends TestCase { public function testFooMethod() { $reflection = new ReflectionClass(Foo::class); $barMethod = $reflection->getMethod('bar'); $barMethod->setAccessible(true); // 保存原方法实现,测试后恢复 $originalMethod = $barMethod->getClosure(); // 重写bar方法,固定返回true $barMethod->setClosure(function () { return true; }); // 执行断言 $this->assertEquals(false, Foo::foo()); // 恢复原方法,避免影响其他测试 $barMethod->setClosure($originalMethod); } }
方案三:使用PHPUnit静态Mock(PHPUnit 9.5+)
若使用新版本PHPUnit,可直接Mock静态方法,但同样要求原代码使用static::bar():
use PHPUnit\Framework\MockObject\MockObject; class FooClassTest extends TestCase { public function testFooMethod() { $mock = $this->createMock(Foo::class); $mock::staticExpects($this->once()) ->method('bar') ->willReturn(true); $this->assertEquals(false, Foo::foo()); } }
总结
优先推荐方案一,修改代码使用延迟静态绑定,既让测试逻辑清晰,也优化了代码的扩展性。若无法修改原代码,反射方案是可靠的替代选择。
内容的提问来源于stack exchange,提问作者Radu Raileanu
相关产品推荐
相关产品推荐

