Testing Protected PHP Methods and Properties

Unit Testing is always an adventure. One problem you run into from time to time is that you need to access a class' protected method or property. While there are loads of tools out there to make testing easier, this is one instance where you can use PHP's native Reflection classes to get the job done.

Accessing a Protected Property

To access a protected property you can use Reflection to set the value as accessible via Reflection like so:

class My_Object {  
    protected $secretProperty = "foo";
}

$object = new My_Object();

$reflection = new \ReflectionClass($object);
$property   = $reflection->getProperty('secretProperty');
$property->setAccessible(true);

echo $property->getValue($object); // "foo"  

Accessing a Protected Method

Accessing a protected method can be done in a similar manner. In this case we generate a \ReflectionMethod object from the main object, set it to accessible, and invoke it with an optional array of method parameters.

class My_Object {  
    protected $secretProperty = "foo";

    protected function secretMethod($append)
    {
        return $this->secretProperty . $append;
    }
}

$object = new My_Object();

$reflection = new \ReflectionClass(get_class($object));
$method = $reflection->getMethod("secretMethod");
$method->setAccessible(true);

echo $method->invokeArgs($object, ["bar"]); // "foobar"  

Pulling it All Together

To make this more useful in testing, you can lump the functionality above into a Trait that can be used as needed in any of your unit tests.

trait AccessProtectedTrait  
{
    /**
     * Call protected/private method of a class.
     *
     * @param object $object    Instantiated object that we will run method on.
     * @param string $methodName Method name to call
     * @param array  $parameters Array of parameters to pass into method.
     *
     * @return mixed Method return.
     */
    public function invokeMethod($object, $methodName, array $parameters = array())
    {
        $reflection = new \ReflectionClass(get_class($object));
        $method = $reflection->getMethod($methodName);
        $method->setAccessible(true);

        return $method->invokeArgs($object, $parameters);
    }

    /**
     * Get protected/private property value
     *
     * @param object|string $object
     * @param string $propertyName
     * @return mixed
     */
    protected function getProtectedProperty($object, $propertyName)
    {
        $reflection = new \ReflectionClass($object);
        $property   = $reflection->getProperty($propertyName);
        $property->setAccessible(true);

        return $property->getValue($object);
    }
}

Using the trait above, you can quickly access protected properties and methods in our tests:

class MyObjectTest extends \PHPUnit_Framework_TestCase  
{
    use AccessProtectedTrait;

    public function testMyObjectProtectedMethods()
    {
        $MyObject = new My_Object();

        $this->assertEquals("foo", $this->getProtectedProperty($MyObject, "secretProperty"));

        $this->assertEquals("foobar", $this->invokeMethod($MyObject, "secretMethod", ["bar"]));
    }
}