Accessing private and protected member variables
Reflection is often used as part of software testing, such as for the runtime creation/instantiation of mock objects. It's also great for inspecting the state of an object at any given point in time. Here's an example of using Reflection in a unit test to verify a protected class member contains the expected value.
Below is a very basic class for a Car. It has a protected member variable that will contain the value representing the color of the car. Because the member variable is protected we cannot access it directly and must use a getter and setter method to retrieve and set its value respectively.
To test this many developers will create a Car object, set the car's color using
Car::setColor(), retrieve the color using
Car::getColor(), and compare that value to the color they set:
On the surface this seems okay. After all, all
Car::getColor() does is return the value of the protected member variable
Car::$color. But this test is flawed in two ways:
- It exercises
Car::getColor()which is out of the scope of this test
- It depends on
Car::getColor()which may have a bug itself which can make the test have a false positive or negative
Let's look at why we shouldn't use
Car::getColor() in our unit test and should use Reflection instead. Let's say a developer is assigned a task to add "Metallic" to every car color. So they attempt to modify the
Car::getColor() to prepend "Metallic" to the car's color:
Do you see the error? The developer used a semi-colon instead of the concatenation operator in an attempt to prepend "Metallic" to the car's color. As a result, whenever
Car::getColor() is called, "Metallic " will be returned regardless of what the car's actual color is. As a result our
Car::setColor() unit test will fail even though
Car::setColor() works perfectly fine and was not affected by this change.
So how do we verify
Car::$color contains the value we are setting via
Car::setColor()? We can use Refelection to inspect the protected member variable directly. So how do we do that? We can use Refelection to make the protected member variable accessible to our code so it can retrieve the value.
Let's see the code first and then break it down:
Here is how we are using Reflection to get the value of
Car::$color in the code above:
- We create a new ReflectionObject representing our Car object
- We get a ReflectionProperty for
Car::$color(this "represents" the
- We make
- We get the value of
As you can see by using Reflection we could get the value of
Car::$color without having to call
Car::getColor() or any other accessor function which could cause invalid test results. Now our unit test for
Car::setColor() is safe and accurate.
Feature detection of classes or objects
Feature detection of classes can partly be done with the
ReflectionClass, also constants can be detected:
method_exists, also an object of the class of interest can be provided instead of the class name. Using reflection, the
ReflectionObject class should be used instead of
Testing private/protected methods
Sometimes it's useful to test private & protected methods as well as public ones.
Easiest way to test drive method is using reflection
If the method is static you pass null in the place of the class instance