One goal of ReFactoring is to push up common implementation into a base class. One effect of this type of refactoring is that adding new objects are easier, because a lot of the common interfaces may already be implemented. However, how does one take advantage of this type of refactoring with UnitTesting?
As I understand refactoring now, it should not only make the actual code easier to work with, but can also make the test code easier to work with. That said, is there a common practice that is used to create a set of tests once that can be run against all new objects they create from a base class? One could then focus on testing new functionality in the subclasses instead of trudging through the same interface tests for the nth-time.
If we need a concrete xUnit to discuss, how about JUnit?
I've found UnitTests push me to use CompositionInsteadOfInheritance, so often this isn't a problem. But what's wrong with writing a single UnitTest for the abstract base class? -- ChristianTaubman
I don't think that is necessarily a bad approach. That is actually how we started with our current project, but I became somewhat concerned that I wasn't testing the interfaces in each subclass, because if its not tested there is some possibility it could be broken. So, maybe it is just a comfort issue. If I am reasonably confident the interfaces won't change, then it should be acceptable to test the common code in the abstract class and only test new interfaces or interfaces I think will change in the concrete classes.
My experience has been that I discover AbstractBaseClasses as I go along; If I try to invent them ahead of time I eventually trip on my own cleverness. In that case, I end up having tests for everything the subclasses need to do, anyway, so when I abstract certain methods upwards I can leave the tests right where they are.
Interesting. I discover them as I go along too, but when I refactor the methods upwards I move the tests along with them. I like to keep the tests close to the implementation. -- ChristianTaubman
I definitely start out with a concrete implementation and only start pushing up code when I find common code shared between more than one class. One additional spin is that I am one of the few people on my team that are actually using UnitTesting at the moment. I think that is where my fear of not testing all subclass interfaces started. I don't think this would be an issue with a team that all agreed on UnitTesting, because any interface changes would have to be tested.
I also tend to prefer Christian's approach, because it uses the tests OnceAndOnlyOnce. If I can avoid duplicating testing code for each new subclass I write, then I am all for it.
One trick you can do... create a test case for the abstract type, using a mock implementation. Write tests for the concrete methods and such. For your subclasses, extend that test case instead of the usual TestCase. If any old tests are inappropriate, perhaps the behaviour shouldn't be in that particular superclass. If inheritance isn't your thing, you could make a test decorator containing the superclass tests, and invoke it from the subclass' test cases with the specific instance as a parameter. Again, if any tests become inappropriate, consider the possibility that the behaviour is in the wrong class. -- WilliamUnderwood
I've been trying out the inheritance approach (with CppUnit style testing) William described and it's making things very nice indeed. -- JoeWeaver
When I search this page for a link to "AbstractTest" I don't find it. What's up with that? -- Phleep
Ahh, yes, that's a page worth looking at. I was imagining the problem here was with common implementation being refactored up though. -- ChristianTaubman
Almost all of my abstract classes got that way due to refactoring common code from concrete classes. The concrete classes are well-covered with unit tests. Hence, the methods moved to abstract classes remain covered by tests. There might be duplicate coverage, but that duplication is part due to the need to document (via tests) the concrete classes. Unless I'm trying to generalize the abstract class into a framework--something my customer (literal or figurative) might not be willing to pay for right now--why should I invest time writing unit tests against non-public abstract classes? --DaveSmith
Abstract base classes do not exist. They are a bug in the C++ type system. -- IvanTkatchev