In Alternative Thoughts on Sniffs for Useless Method Overriding I discussed the discovery of a horrible dance that some developers will do just to test private functions:
- See a
privatemethod you want to test in isolation - Upgrade the
privatetoprotected - Create a child class implementation of this class in the
TestCasefile - Override any
protectedmethods, that were actually intended to beprivatemethods and make them nowpublic - Test the new child class instead of the actual class
This violates the only one class per test file rule, as discussed in PHPUnit Coding Standard: Classes
Many claim that they do this because they do not want the methods to be part of the public API, but need to make it protected so they can do this hack. Yet those who argue for this so called solution have nothing to reconcile the fact that they have removed the ability to distinguish between methods that should be override-able by child implementations and those that should not.
In all honesty, if you have functionality that you would prefer not to be in the public API of one object, but it is sufficiently complex to warrant it’s own battery of tests, you should take that as a suggestion to pull that functionality out into it’s own object.
It is a pity to hide something so complex in a private function, and tempt others into exercising copy/paste coding practices to use it.
If you are using PHP 5.3, then you can do another cheat that will let you avoid the number of class violations, and let you keep using the private keyword. Sebastian Bergmann discusses this in Testing Your Privates.
Don’t jump the gun. This is still not license to blindly make things private. Even Sebastian repeats in the post and in the comments:
Just because the testing of protected and private attributes and methods is possible does not mean that this is a “good thing”.
So why am I even talking about these naughty ways to test private methods?
Because I cannot necessarily stop you!
So let me offer an alternative to all of this that should help communicate intent in your code, let you use all the accessibility levels, and keep you from re-hashing reflection calls in your test methods.
Today I put out a new release of the Etsy PHPUnit Extensions. This new release includes PHPUnit_Extensions_Helper_AccessibleObject.
PHPUnit_Extensions_Helper_AccessibleObject is a wrapper for classes which contain private or protected methods that you would like to test in isolation, for some bizarre reason. But, you may only call private and protected methods of the wrapped object that are annotated with @accessibleForTesting.
Why the special @accessibleForTesting annotation on methods in my production code?
With the @accessibleForTesting you will be able to communicate with other developers that while this code is private or protected you intend to test the logic in it. Also, we can easily comb through the code with a static analyzer to find spots in our code that we might want to consider extracting into more reusable places, like another class. Furthermore, we can also sniff for usages of Reflection.* in our tests and useless method overrides, then double back to ensure we move everything to this AccessibleObject method for testing private and protected methods so that we can go back to the loveliness mentioned in the first half of this paragraph.
If you were making the argument for doing the horrible dance to test private methods mentioned in the beginning of this article, then you can certainly get onboard with being more descriptive about your intent with @accessibleForTesting.
For examples, usage, and code: https://github.com/etsy/phpunit-extensions/wiki/Helpers
P.S. This is still naughty! But it’s a compromise I think I might be able to live with.