phpunit - mockbuilder - set mock object internal property

2022-08-30 10:24:33

是否可以使用禁用的构造函数和手动设置的受保护属性创建模拟对象?

这是一个愚蠢的例子:

class A {
    protected $p;
    public function __construct(){
        $this->p = 1;
    }

    public function blah(){
        if ($this->p == 2)
            throw Exception();
    }
}

class ATest extend bla_TestCase {
    /** 
        @expectedException Exception
    */
    public function testBlahShouldThrowExceptionBy2PValue(){
        $mockA = $this->getMockBuilder('A')
            ->disableOriginalConstructor()
            ->getMock();
        $mockA->p=2; //this won't work because p is protected, how to inject the p value?
        $mockA->blah();
    }
}

所以我想注入受保护的p值,所以我不能。我应该定义 setter 还是 IoC,或者我可以使用 phpunit 来做到这一点?


答案 1

可以使用反射使属性公开,然后设置所需的值:

$a = new A;
$reflection = new ReflectionClass($a);
$reflection_property = $reflection->getProperty('p');
$reflection_property->setAccessible(true);

$reflection_property->setValue($a, 2);

无论如何,在您的示例中,您不需要设置 p 值即可引发异常。您正在使用模拟来控制对象行为,而不考虑其内部。

因此,您不必设置 p = 2 以引发异常,而是将模拟配置为在调用 blah 方法时引发异常:

$mockA = $this->getMockBuilder('A')
        ->disableOriginalConstructor()
        ->getMock();
$mockA->expects($this->any())
         ->method('blah')
         ->will($this->throwException(new Exception));

最后,奇怪的是,你在ATest中嘲笑A类。您通常会模拟正在测试的对象所需的依赖项。

希望这有帮助。


答案 2

以为我会留下一个方便的助手方法,可以快速复制并粘贴在这里:

/**
 * Sets a protected property on a given object via reflection
 *
 * @param $object - instance in which protected value is being modified
 * @param $property - property on instance being modified
 * @param $value - new value of the property being modified
 *
 * @return void
 */
public function setProtectedProperty($object, $property, $value)
{
    $reflection = new ReflectionClass($object);
    $reflection_property = $reflection->getProperty($property);
    $reflection_property->setAccessible(true);
    $reflection_property->setValue($object, $value);
}

推荐