phpunit - mockbuilder - 设置模拟对象内部属性

inf*_*rno 42 php phpunit

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

这是一个愚蠢的例子:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

所以我想注入受保护的p值,所以我不能.我应该定义setter或IoC,还是我可以用phpunit来做这个?

gon*_*lez 53

您可以使用Reflection将属性设为公共属性,然后设置所需的值:

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

$reflection_property->setValue($a, 2);
Run Code Online (Sandbox Code Playgroud)

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

因此,不是设置p = 2而是引发Exception,而是在调用blah方法时将mock配置为引发异常:

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

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

希望这可以帮助.

  • 不要在测试中使用依赖注入容器!一个好的单元测试只测试一个类,所有依赖项都作为完全配置的模拟注入.如果你不能这样做,你就会有一个糟糕的架构,应该加以改进. (4认同)
  • A 类没有完全依赖注入,我在它的构造函数中创建了几个类的新实例...所以我需要重写构造函数来模拟这些实例。这不是最好的方法,我想我会使用依赖注入容器而不是这个。 (2认同)

小智 19

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

/**
 * 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);
}
Run Code Online (Sandbox Code Playgroud)