I have a skeletal implementation, as in Item 18 from Effective Java (extended discussion here). It is an abstract class that provides 2 public methods methodA() and methodB() that call subclasses methods to "fill the gaps" that I can't define in an abstracted manner.
I developed it first by creating a concrete class and writing unit tests for it. When the second class came, I was able to extract common behavior, implement the "missing gaps" in the second class and it was ready (of course, the unit tests were created for the second subclass).
Time went by and now I have 4 subclasses, each implementing 3 short protected methods that are very specific to their concrete implementation, while the skeletal implementation does all the generic work.
My problem is that when I create a new implementation, I write the tests all over again:
- Does the subclass calls a given required method?
- Does a given method has a given annotation?
- Do I get the expected results from the method A?
- Do I get the expected results from the method B?
While see advantages with this approach:
- It documents the subclass requirements through tests
- It may fail fast in case of a problematic refactoring
- Test the subclass as a whole and through their public API ("does methodA() work?" and not "does this protected method work?")
The problem I have is that the tests for a new subclass are basically a no-brainer: - The tests are all the same in all subclasses, they just change the return type of the methods (the skeleton implementation is generic) and the properties I check on the assert part of the test. - Usually the subclasses have no tests that are specific for them
I like the way the tests focus on the result of the subclass itself and protects it from refactoring, but the fact that implementing the test became just "manual work" makes me think I am doing something wrong.
Is this issue common when testing class hierarchy? How can I avoid that?
ps: I thought about testing the skeleton class, but it also seems strange creating a mock of an abstract class to be able to test it. And I think that any refactoring on the abstract class that changes behavior that is not expected by the subclass would not be noticed as fast. My guts tell me that testing the subclass "as a whole" is the preferred approach here, but please feel free to tell me I am wrong :)
ps2: I've googled and found many questions about the subject. One of them is this one which has a great answer from Nigel Thorne, my case would be his "number 1." That would be great, but I can't refactor at this point, so I would have to live with this problem. If I could refactor, I would have each subclass as a strategy. That would be OK, but I would still test the "main class" and test the strategies, but I would not notice any refactoring that breaks the integration between main class and strategies.
ps3: I've found some answers saying that "it is acceptable" to test the abstract class. I agree that this is acceptable, but I would like to know which is the preferred approach in this case (I am still starting with unit tests)