Testing and Inheritance Inheritance Inheritance related bugs

					    Testing and Inheritance                                  Inheritance
      Should you retest inherited methods?                     In the early years people thought that
                                                               inheritance will reduce the need for testing
      Can you reuse superclass tests for
                                                                 Claim 1: If we have a well-tested superclass, we
      inherited and overridden methods?                          can reuse its code (in subclasses, through
      To what extent should you exercise                         inheritance) with confidence and without retesting
                                                                 inherited code”
      interaction among methods of all
                                                                 Claim 2: “A good-quality test suite used for a
      superclasses and of the subclass under                     superclass will also be good for a subclass”
      test?                                                    Both claims are wrong.

                                                     1                                                          2

    Inheritance-related bugs                                 Inheritance-related bugs
      Incorrect Initialization                                 Inadvertent Bindings
class Parent{             class Child{                   class Parent{      What will be printed?
  public void reset(){      void initialize(){             int a=5;         Is it the intended value?
    initialize();             //Initialize               }
    …                         //Child’s
                                                         class Child{             class Driver{
  }                           //attributes
                                                           int a=3;                 …
  void initialize(){        }
                                                           void print(){            Child c =
    //Initialize          }
                                                             System.out.print(a);     new Child();
    //Parent’s            reset will work incorrectly      }                        c.print();
    //attributes          in Child unless initialize     }                        }
  }                       calls its superclass version
}                                                    3                                                          4
Inheritance-related bugs                                   Inheritance-related bugs
 Missing Override                                            Direct access to superclass fields from the
   A subclass omits to provide a specialized                 subclass code
   version of a superclass method                              Changes to the superclass implementation can
                                                               create subclass bugs
   Subclass objects will have to use the                       Subclass bugs or side effects can cause failure in
   superclass version, which might not be                      superclass methods
   appropriate                                                 If a superclass is changed, all subclasses need to
   E.g. method equals in Object tests for                      be tested
   reference equality. In a given class, it                    If a subclass is changes, superclass features used
   might be right to override this behaviour                   in the subclass must be retested

                                             5                                                                 6

Inheritance-related bugs                                   An example
 Square Peg in a Round Hole                                  Consider class Rectangle below
   Design Problem
                                                   class Rectangle{
   A subclass is incorrectly located in a            public void setWidth(double w) {itsWidth=w;}
   hierarchy                                         public void setHeight(double h) {itsHeight=w;}
   Liskov substitution principle: Functions that     public double getHeight() {return itsHeight;}
   use references to base classes must be            public double getWidth() {return itsWidth;}
   able to use objects of derived classes               private double itsWidth;
   without knowing it.                                  private double itsHeight;
                                             7                                                                 8
   An example                                      Problems with this design
     Assume that the system containing              Do not need both itsHeight and itsWidth
     Rectangle needs to deal with squares as        setWidth and setHeight can bring a
     well                                           Square object to a corrupt state (when
     Since a square is a rectangle, it seems        height is not equal to width)
     to make sense to have a new class                           class Square{
     Square that extends Rectangle                                 setWidth(double w){
     That very “reasonable” design can                One
     cause some significant problems                  solution
                                                                   // Similar for setHeight
                                               9                 }                                    10

   Not really a solution                           What went wrong?
     Consider this client code                      The Liskov substitution principle was violated
Rectangle r;                                          If you are expecting a rectangle, you can not
…                                                     accept a square
r.setWidth(5);                                      The overridden versions of setWidth and
r.setHeight(4);                                     setHeight broke the postconditions of their
assert(r.getWidth() * r.getHeight()) == 20);        superclass versions
                                                    Isn’t a square a rectangle? Yes, but not when
     The problem is definitely not with the         it pertains to its behaviour
     client code
                                              11                                                      12
Inheritance-related bugs                             Inheritance-related bugs
     Worm Holes                                           Fat Interface
       A subclass violates an invariant from the            A subclass inherits methods that are
       superclass, or creates an invalid state              inappropriate or irrelevant
     Spaghetti Inheritance                          class BigDecimal{
       Complicated multiple inheritance may lead      BigDecimal movePointLeft(int n) {..}
       to the wrong variable or method being
       inherited                                    class Money extends BigDecimal{
                                                      // movePointLeft should probably be
                                                      // overridden to do nothing
                                               13                                                  14

Three Testing Axioms                                 Three Testing Axioms
1.   Antiextensionality                              2.    Antidecomposition
       A test suite that covers one                          Coverage achieved for a module under
       implementation of a specification does not            test is not necessarily achieved for
       necessarily cover a different                         modules it calls
       implementation of the same specification
                                                             Coverage of class Account does not
       Test cases that cover method
       insertNewElement in class Parent, do                  guarantee coverage of classes Money or
       not necessarily cover                                 Date that Account uses
       insertNewElement in class Child, even
       if the responsibilities are identical
                                               15                                                  16
                                                          Subclass Development
Three Testing Axioms                                      Scenarios and How to Test
3.   Anticomposition                                       Superclass modifications
      Test suites that are individually adequate             Must retest changed methods in superclass
      for segments within a module are not                   Also must retest the changed methods in the
      necessarily adequate for the whole                     context of subclasses (by anticomposition)
      module                                               Subclass modifications
        Individual method scope test suites do not
                                                             Must also retest methods inherited from a
        guarantee all intra-class paths are tested
        Can’t be confident in a subclass that has only
        passed subclass testing, even if its superclass      In the following example, we must retest method
        has passed its class testing                         calculateInterest in the subclass

                                                    17                                                     18

An example                                                Another example
 class Account{                                           class A {
   Money balance;                                           int x; // invariant: x > 100
   void calculateInterest(){                                void m() { // correctness depends on
     // Use balance for the calculation                                // the invariant } }
                                                          class B extends A {
                                                             void m2() { x = 1; }        }
 class InvestmentAcc extends Account{
   void sell(){ // Subclass method                         If we add a new method m2 that has a bug
     balance = balance.update();                           and breaks the invariant, method m is
   }                                                       incorrect in the context of B even though it is
 }                                                         correct in A
                                                              Therefore, m should be tested in B       20
Subclass Development
Scenarios and How to Test
 Overriding a superclass method used by a
 superclass method
    Overriding can produce indirect effects on
    another inherited method
    In the following example we cannot assume
      method rollover is correct for any of the
      subclasses of Account
      Reusing test cases for rollover will give us
      the same amount of coverage in a sublass of
                                                 21   22

Another Example
 class A {
 void m1() { …; m2(); … }
 void m2() { … }    }
 class B extends A {
      void m2() { … } }
If inside B we override a method from
A, this indirectly affects other methods
inherited from A
  E.g. method m1 calls B.m2, not A.m2: so,
  we cannot be sure that m1 is correct
  anymore and we need to retest it inside B 23        24
Effect of Inheritance on
Testing?                                         Inheriting Class Test Suites
 Does not reduce the volume of test               It is essential to run superclass test suites on
 cases                                            subclasses
                                                    Not all tests might apply (e.g. if they exercise a
 Rather, number of interactions to be               private method)
 verified goes up at each level of the              Must add additional tests (for subclass and
 hierarchy                                          interactions between superclass and subclass)
                                                  If a superclass test case cannot apply to a
                                                  subclass, we have a violation of the Liskov
                                                  substitution principle

                                            25                                                           26

Goals of Flattened Class Testing                 Flattened class test patterns
 Retest superclass methods only to the extent     Polymorphic Server Test
 necessary to gain confidence that inherited        Verify LSP compliance for a polymorphic
 features work correctly in the context of the      server hierarchy
                                                    Suitable for nonmodal classes
 Reuse superclass test suites as much as
 possible.                                        Modal Hierarchy Test
 Extend class test suites with test cases that      How to design a test suite that validates
 are effective for the kind of bugs that            the flattened state model
 accompany inheritance.                             Suitable for modal classes

                                            27                                                           28
Polymorphic Server Test                                 An example
  Consider all test cases that exercise                 class TestAccount{
                                                          public void setUp(){
  polymorphic methods at method scope                       Account a = new Account();
  level                                                   }
  According to LSP, these should apply at                 public void testDeposit(){…}
  every level of the inheritance hierarchy              }

  Expand each test case into a set of test              class TestSavingsAccount
  cases, one for each polymorphic                                        extends TestAccount{
                                                          public void setUp(){
  variation                                                 Account a = new SavingsAccount();
                                                        }                                             30

Another example                                         Modal Hierarchy Test
If deposit is a polymorphic method declared in            We saw a number of ways that the
class Account, then method scope tests for it in          state model of a subclass can differ
class SavingsAccount, should apply to Account
objects as well
                                                          from that of the superclass
class TestSavingsAccount{
                                                          We need to ensure that none of the
  public void testDeposit(){                              following happen
    Account a = new SavingsAccount();                       A valid superclass transition is rejected
    doTestDeposit(a);                                       An illegal superclass transition is accepted
    a = new Account();
    doTestDeposit(a);                                       An incorrect action is produced
}                                                  31                                                 32
Modal Hierarchy Test


Shared By: