PHPUnit tests with Peek and Poke - No more Reflection

Sometimes you want to test your protected or private methods or need access to a class property. This can be achieved via the PHP ReflectionClass, but it's some lines of code. Sebastian Bergmann has created a new library Peek and Poke, which does the job. See this tutorial how to use it.

Some people would argue that only public methods should be tested. That's right, but if you have a protected/private method, which do several stuff, it's useful to test this method detached from other tests with a data provider.

The class to test

Assume we have the following class which should be tested. Note that this demonstrates only the usage of Peek and Poke.

class AwesomeClass  
{
    protected $protectedProperty;

    private $privateProperty;

    public function __construct(array $data)
    {
        // do stuff
    }

    protected function setProtectedProperty($value)
    {
        $this->protectedProperty = $value;
    }

    private function setPrivateProperty($value)
    {
        $this->privateProperty = $value;
    }
}

The old PHP ReflectionClass way

To see how easy it is to access not public methods or properties with Peek and Poke we start with a common PHPUnit test ReflectionClass example. This test case shows only the private method test, because the protected method test has the same structure.

class AwesomeClassTest extends \PHPUnit_Framework_TestCase  
{
    public function testSetPrivateProperty()
    {
        $stub = $this->getMockBuilder('AwesomeClass')
            ->disableOriginalConstructor()
            ->getMock();

        $reflectionClass = new \ReflectionClass('AwesomeClass');

        $reflectionMethod = $reflectionClass->getMethod('setPrivateProperty');
        $reflectionMethod->setAccessible(true);

        $expected = 'my value';

        $reflectionMethod->invoke($stub, $expected);

        $reflectionProperty = $reflectionClass->getProperty('privateProperty');
        $reflectionProperty->setAccessible(true);

        $this->assertSame($expected, $reflectionProperty->getValue($stub));
    }
}

The Peek and Poke way

Private properties does not work with mock objects.

Now let's take a look at the same test method with Peek and Poke. One limitation is, that it doesn't works with private properties and mock objects, but it works with protected properties and mock objects.

class AwesomeClassTest extends \PHPUnit_Framework_TestCase  
{
    public function testSetPrivateProperty()
    {
        // private properties does not work with mock objects
        $stub = new \AwesomeClass(array());

        $expected = 'my value';

        $proxy = new \SebastianBergmann\PeekAndPoke\Proxy($stub);
        $proxy->setPrivateProperty($expected);

        $this->assertSame($expected, $proxy->privateProperty);
    }

    public function testSetProtectedeProperty()
    {
        // protected properties works with mock object
        $stub = $this->getMockBuilder('AwesomeClass')
            ->disableOriginalConstructor()
            ->getMock();

        $expected = 'my value';

        $proxy = new \SebastianBergmann\PeekAndPoke\Proxy($stub);
        $proxy->setProtectedProperty($expected);

        $this->assertSame($expected, $proxy->protectedProperty);
    }
}

Conclusion

The Peek and Poke library reduces the lines of code and makes access to protected and private methods and properties easier as with the PHP ReflectionClass. The only reason to use the PHP ReflectionClass is, when you have a mock object and need access to a private property.

If you have some suggestions, please leave a comment.

Discuss (0 Comments)