Consider the following simple class hierarchy in Java
class Foo {
protected void someMethod(Bar bar) {
...
}
protected void someOtherMethod(Baz baz) {
...
}
}
class EnhancedFoo extends Foo {
@Override
protected void someMethod(Bar bar) {
...
}
}
I now start writing JUnit unit tests for these two classes. Since the contracts for the method someMethod
are same for both the classes, I need basically exactly the same test methods (concerning the someMethod
method) for both the classes, which leads to code duplication. Doing this for a much richer class hierarchy with multiple overwritten methods, it just feels like inheritance is bad for testability.
Also, even though the method someOtherMethod
is not overridden in ExtendedFoo
, I need to include the pertaining tests for ExtendedFoo
because that is still the contract and this extended class and unit tests should test this.
Is there some other way of organizing hierarchies in Java that is better for testability? Is there some JUnit construct that would help alleviate this problem?
If you really need to extend the base class to override something, extract the functionality to a third class and make the extended class a proxy. The extended class does nothing else than call methods on the third class. E.g. This way, you can test the real implementation, without instantiating the base class.
Unit testing abstract classes leads to the same consequences as unit testing private methods. In fact, these two practices are essentially the same anti-pattern. Both couple your tests to implementation details and therefore increase the noise you have to deal with after each refactoring.
Inheritance definitionInheritance in OOP is achieved when one object acquires (inherits) the properties and the behaviors of the parent object. This means that the protected and public members of the parent class can be reused in the derived class, without having to define them all over again.
One approach we used when we had a very similar scenario was to also reuse the est classes:
class FooTest {
@Test
public void testSomeMethodBar() {
...
}
@Test
public void void someOtherMethodBaz(Baz baz) {
...
}
}
And extend it for subclass tests:
class EnhancedFooTest extends FooTest {
@Test
public void testSomeMethodBar() {
...
}
}
JUnit
will run this specific overridden test method, and also the other default tests in FooTest
. And that eliminates unnecessary duplication.
Where appropriate, some test classes are even declared abstract
, and are extended by concrete test classes (tests for concrete classes).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With