0

Are there best practices for testing classes that use inheritance?

For example if I have a class BaseNode

public class BaseNode
{
    int testInt;//attribute

    //assume getters and setters and constructor

    public function somethingComplicated()
    {
        //put complex code here
    }
}

and another class called InputNode that inherits from BaseNode.

public class InputNode extends BaseNode
{
    int secondTest;//attribute

    //assume getters and setters and constructor
}

If I write getters and setters for both classes, what would I need to test?

Do you really have to write tests for getTestInt() in both the BaseNode and the InputNode classes? If you write a test for getTestInt() in the InputNode class does that automatically count as a test for the BaseNode class as well?

Edited question to make it more specific. I'm not just asking about getters and setters.

If you write a test for somethingComplicated() in the Base class do you automatically assume that the test is valid for the InputNode class?

I originally used getters and setters as a simple example to try and illustrate the concept. I didn't mean that I only wanted to test the getters and setters.

j.jerrod.taylor
  • 1,120
  • 1
  • 13
  • 33
  • Related: [Unit testing accessors (getters and setters)](http://stackoverflow.com/questions/4987882/unit-testing-accessors-getters-and-setters) – deceze May 23 '13 at 13:37
  • 1
    @deceze I edited the question to make it more specific. I just used getters and setters as an example but I really want to know the answer for more complex things as well. – j.jerrod.taylor May 23 '13 at 13:46

3 Answers3

2

First of all, setters are a code smell, in no small part because they increase the complexity of testing. They do this because they potentially increase number of possible states an object can be in.

The best practices are:

  1. Minimize setters
  2. Every derived object has to pass every base class test, so make sure you set your tests up to do just that. If they don't pass, then that means that base instances are not liskov substitutable. The upside of this is that you shouldn't need to write a complete set of new tests for every derived class.
  3. You'll need tests to specifically ensure that your new setters and behaviours don't interact incorrectly with base class behaviour.
Marcin
  • 48,559
  • 18
  • 128
  • 201
1

You have to test the base class too, because the base node's method/getter could be affected by code in the base class that is:

  • overriden by derived classes
  • code in derived classes may cause side effects (by changing base class fields, etc.)
FastAl
  • 6,194
  • 2
  • 36
  • 60
1

The full suite of tests for the base class needs to pass for each derived class in order to demonstrate Liskov substitutability. In addition, tests for the specialization provided by the derived class need to exist. Many folks choose to organize the test classes in a way that mirrors the classes under test for just this reason.

Given a class InputNode that subclasses BaseNode, we might have test classes that look like this [corrections to my Java syntax are welcome]:

public class BaseNodeTest {
    protected BaseNode getBaseNode () {
        return new BaseNode();
    }

    public void test_complicated_BaseNode_behavior () {
        BaseNode bn = getBaseNode();
        // actual test code...
    }
}

public class InputNodeTest extends BaseNodeTest {
    protected BaseNode getBaseNode () {
        return new InputNode();
    }

    public void test_InputNode_specific_behavior () {
        InputNode in = getBaseNode();
        // actual test code...
    }
}

The key point is that InputNodeTest will run all of the tests in BaseNodeTest against InputNode and add its own tests. This assures that InputNode could be used wherever a BaseNode is expected.

darch
  • 4,200
  • 1
  • 20
  • 23