TDD: Testing sub classes
We ran into another interesting testing dilemma while refactoring the view model code which I described in an earlier post to the point where we have an abstract class and three sub classes which means that we now have 3 classes which did the same thing 80% of the time.
As I mentioned in a post a couple of weeks ago one of the main refactorings that we did was to move some calls to dependency methods from the constructor and into properties so that those calls would only be made if necessary.
After we'd done this the code looked a bit like this:
public abstract class ParentModel { private readonly Dependency1 dependency1; ... public decimal Field1 { get { return dependency1.Calculation1(); } } public decimal Field2 { get { return dependency1.Calculation2(); } } } public class BusinessProcess1Model : ParentModel { } public class BusinessProcess2Model : ParentModel { } public class BusinessProcess3Model : ParentModel { }
We wanted to ensure that the tests we had around this code made sure that the correct calls were made to 'depedency1′ but because ParentModel is an abstract class the only way that we can do this is by testing one of its sub classes.
The question is should we test this behaviour in each of the sub classes and therefore effectively test the same thing three times or do we just test it via one of the sub classes and assume that's enough?
Neither of the options seems really great although if we cared only about behaviour then we would test each of the sub classes independently and forget that the abstract class even exists for testing purposes.
While the logic behind this argument is quite solid we would end up breaking 3 tests if we needed to refactor our code to call another method on that dependency for example.
I suppose that makes sense in a way since we have actually changed the behaviour of all those classes but it seems to me that we only really need to know from one failing test that we've broken something and anything beyond that is a bit wasteful.
In C# it's not actually possible for 'Field1′ or 'Field2′ to be overriden with an alternate implementation unless we defined those properties as 'virtual' on the 'ParentModel' which we haven't done.
We could however use the 'new' keyword to redefine what those properties do if the callee had a reference directly to the sub class instead of to the abstract class which means it is possible for a call to 'Field1′ to not call 'dependency1′ which means that maybe we do need to test each of them individually.
I'm not sure which approach I prefer, neither seems better than the other in my mind.
IMHO, since the functionality for Field1 and Field2 are not virtual, it would be reasonable enough to just test them through a test-specific derivation of ParentModel.
As for the possibility of derived classes using 'new' to declare their own Field1 and Field2, if and when that were to happen you could add a test for it, especially given that no code referencing one of those derived instances as a ParentModel would invoke the 'new' version, only code explicitly referencing the derived type would.
Jeremy Gray
14 Sep 09 at 1:41 am
You are right, though, that neither option feels all that great but that's just the nature of abstract classes.
/
Jeremy Gray
14 Sep 09 at 1:42 am
We ran into the same kind of dilemma (in Java, though) and our approach was to have an anonymous class that inherits from your abstract parent, making it possible to test it in isolation and without the need to know about the subclasses.
I don't work with .NET though, so I'm not sure if such thing would be possible in your case.
Leonardo Borges
14 Sep 09 at 3:22 am
@Leonardo/Jeremy – I was talking with my colleague Liz about this and she suggested the test specific class as well.
In .NET you would have to create an actual class to do it which is not quite as clean as in Java but it would still do the job.
I had written that option off because it feels kinda nasty though since that class your now testing is there just to help testing.
Maybe it's just the punishment you get for using abstract classes
Mark Needham
14 Sep 09 at 7:09 am
[...] This post was mentioned on Twitter by planettw. planettw said: Mark Needham: TDD: Testing sub classes: We ran into another interesting testing dilemma while refactoring the vi.. http://bit.ly/1QGzUt [...]
Tweets that mention TDD: Testing sub classes at Mark Needham -- Topsy.com
14 Sep 09 at 7:36 am
two more options
1) use composition instead of inheritance
2) If you must use inheritance, use it in your tests too. Create an abstract *test* class that tests the similar behaviour and then extend that class for each of your tests. You would then break three tests with a change but you can fix them all in one place.
Perryn Fowler
14 Sep 09 at 5:01 pm
Hi Mark,
If I ever create abstract classes, I tend to have a test hierarchy that mirrors them.
So if, for instance I have three classes called:
Animal
Cat
Dog
I'll create three tests:
abstract AnimalTest
CatTest extends AnimalTest
DogTest extends AnimalTest
(If necessary, I change these names so that whatever build I'm using doesn't pick up the animal test).
The AnimalTest describes the behaviour that's common to all animals, and uses an abstract method to create the animal. The CatTest and DogTest then override this method.
This describes the common behaviour quite well, while ensuring that both the Cat and Dog carry out their respective responsibilities here.
The methods in the abstract AnimalTest are never run on their own, only as a component of the other two.
Liz Keogh
14 Sep 09 at 6:20 pm
@Liz, @Perryn – I like that idea of having the test hierarchy matching the code, sounds like it'd work out well – I've not come across that approach before, thanks for sharing.
@Perryn – yeh I was wondering whether the struggling was deserved for using inheritance as well
Might actually have been better off using composition.
Mark Needham
14 Sep 09 at 6:26 pm
You could use something like parametrized test (PEX) or an data-based test.
public TestBlabla(AbstractBaseClass classToTest)
{
…
}
an the input for classToTest are the subclasses.
Tobias
15 Sep 09 at 10:31 pm
I think what you want is AbstractTestCase which I first read about in JB Rainsberger's "JUnit Recipes." Take a look at http://c2.com/cgi-bin/wiki?AbstractTestCases
Pat Maddox
17 Sep 09 at 7:49 am
[...] a post I wrote earlier in the week I described a dilemma we were having testing some code which made use of abstract classes and Perryn Fowler, Liz Keogh and Pat Maddox pointed out that a useful approach for this problem [...]
TDD: Testing with generic abstract classes at Mark Needham
18 Sep 09 at 12:41 am