Tuesday, February 16, 2010

Testing protected members

This is a follow-up to Testing private members.

In the previous post, we discussed how to make sure that code in a private method is tested without accessing it directly, since that would imply the use of reflection and a violation of the class contract (its public methods or its interface.)
Following the same procedure, let's find out why we end up with protected methods in our classes.
For starters, we should indeed write a method only if really necessary, to keep production code as simple and short as possible. This requirement is usually made explicit in a [unit] test and thereby automatically checked. Of course only public methods are directly called, while protected and private ones are called by public methods we thoroughly test (otherwise they would be simply deleted as unused code).
Though, there is a way a protected method can be reached from client code that a private one cannot: inheritance. Availability through inheritance is in fact the trait that distinguish protected class members from private ones.
Thus, there are two possible situations:
  • the method is tested because at least a public method calls it. Given that the method logic does not warrant an external class, this arrangement would be fine.
  • the method is intended to be called by Client subclasses, so it is not tested.
The latter case is the tricky one and there are two solutions I propose:
  • write a simple subclass in a test case's source file and instantiate that class to test the protected method;
  • refactor the method with a public signature on a collaborator class that will be passed to the constructor of the Client, effectively favoring object composition over an inheritance-based Api.
The second solution is by far the best one if the protected method contains real logic.
Providing functionality with inheritance seems useful by a raw count of the lines of code saved, but it is really an hassle as an application grows. First, in most languages only one parent is allowed for a class, and this restriction leads to long hierarchies where one never knows where a method is defined and overridden. For example, in Java a JFrame is a Frame, which is a Window, which is a Container, which is a Component, which is an Object.
Second, it leaves too many responsibilities on a Client class, which cannot select what members to inherit and if it could, it would not be able to discern the dependencies between different methods. Too much inheritance bloats the Api of the subclasses (the aforementioned JFrame has more than 300 public methods, and only 30 are not inherited) and the parent classes' one if they try to accomodate uncohesive features.
Finally, inheritance forces a dependency on a concrete class. For example historically, Object-Relational Mappers implemented an Active Record pattern. Now they are moving to a less invasive approach based on Plain Old Java/Php/.Net objects, without forcing the end user to inherit from their abstract classes and allowing him to play with objects without referencing a database.
With a moderate use of inheritance, there are really few good reasons to test a protected method per se. I hope from now on you will take a look at composition and dependency injection, which are dynamic dependencies, instead of always setting up static links between classes such as the extends keyword.

No comments:

Post a Comment