Why you should not use Reflection when testing
with some code examples
Found a typo? Edit meIt causes bugs when refactoring due to the high coupling
When we use reflection, our tests get too fragile, we are allowing our tests to know so much information about the real implementation. We need to hardcode the method name, and we are coupling our test method to the production code. Furthermore, we need to write a lot about boilerplate to test a simple method.
- Question: I need to get at least 80% of code coverage, how can I get it without the Reflection class?
- Answer: You should test ONLY your public methods, and depending on the variables we pass, we should reach all the possible paths.
So, we do not need to test the private methods per se; they are called indirectly from our public functions.
Comparison between standard and Reflection tests
What would happen if we modified the simpleOperation()
method from the Addition class to the following, and we run the
tests?
- Question: Will the ‘standard’ fail? What about the Reflection one?
- Answer: The standard will fail because we expect 4 as a result, but we got a 0. However, the reflection will pass, because we are not focusing on the real implementation, we are creating a false positive which can be dangerous.
Dealing with some problems when not using Reflection
Let me show you a more realistic example (idea from PHPTheRightWay):
final readonly
To test the vehicle’s model is easy, but what about the price?
One possible solution could be using the Reflection class to be able to set explicitly the price like:
But our Vehicle class is final, so we cannot perform this test, also, we said we shouldn’t use the Reflection class, so, probably we are doing something wrong in this class (TIP: you should make final your classes by default 😉).
- Question: So what is the problem here?
- Answer: The problem here is that we are performing an action inside the class (on the constructor) to which we do not have access from the outside.
One solution could be to inject the value in the constructor like:
final readonly
And when we want to create this class, we could pass the final price as:
$vehiclePrice = ;
$vehicle = new ;
So, our price test could be like:
To sum up, I don’t recommend using the Reflection class anywhere in your code unless you are very aware of what you are doing, usually, there are alternative implementations to what you want to achieve without using it.
Additionally, defining our classes as final
helps us to have a better design, not only because it forbid us the use of
Reflection, but also it prevents us from mocking our business logic, which is good.