06 Test Code Patterns - skylerto/Software-Testing GitHub Wiki

Test Code Patterns

Testing and Inheritance

  • Should you retest inherited methods?
  • Can you reuse superclass tests for inherited and overridden methods?
  • To what extent should you exercise interaction among methods of all super classes and of the subclass under test?

Inheritance

  • In the early years people thought that inheritance will reduce the need for testing
    • Claim 1: “If we have a well-tested super class, we can reuse its code (in subclasses, through inheritance) with confidence and without retesting inherited code”
    • Claim 2: “A good-quality test suite used for a superclass will also be sufficient for a subclass”
  • Both claims are wrong.

Inheritance-related bugs

  • Missing Override
    • A subclass omits to provide a specialized version of a superclass method
    • Subclass objects will have to use the superclass version, which might not be appropriate
    • E.g., method equals in Object tests for reference equality. In a given class, it might be right to override this behaviour

Inheritance-related bugs

  • Direct access to superclass fields from the subclass code
    • Changes to the superclass implementation can create subclass bugs
    • Subclass bugs or side effects can cause failure in superclass methods
    • If a superclass is changed, all subclasses need to be tested
    • If a subclass is changed, superclass features used in the subclass must be retested

Testing of Inheritance

  • Principle: inherited methods should be retested in the context of a subclass
  • Example 1: if we change some method m in a superclass, we need to retest m inside all subclasses that inherit it

Inheritance-related bugs

  • Square Peg in a Round Hole
    • Design Problem
    • A subclass is incorrectly located in a hierarchy
    • Liskov Substitution Principle (LSP): Functions that use references to base classes must be able to use objects of derived classes without knowing it.

Effect of Inheritance on Testing?

  • Does not reduce the volume of test cases
  • Rather, number of interactions to be verified goes up at each level of the hierarchy

Polymorphic Server Test

  • Consider all test cases that exercise polymorphic methods
  • According to LSP, these should apply at every level of the inheritance hierarchy
  • Expand each test case into a set of test cases, one for each polymorphic variation

Testing abstract classes

  • Abstract classes cannot be instantiated
  • However, they define an interface and behaviour (contracts) that implementing classes will have to adhere to
  • We would like to test abstract classes for functional compliance
    • Functional Compliance is a module's compliance with some documented or published functional specification

Functional vs. syntactic compliance

  • The compiler can easily test that a class is syntactically compliant to an interface
    • All methods in the interface have to be implemented with the correct signature
  • Tougher to test functional compliance
    • A class implementing the interface java.util.List may be implementing get(int index) or isEmpty() incorrectly
  • Think LSP...

Abstract Test Pattern

  • This pattern provides the following
    • A way to build a test suite that can be reused across descendants
    • A test suite that can be reused for future as- yet-unidentified descendants
      • Especially useful for writers of APIs.

Abstract Test Rule 1

-Write an abstract test class for every interface and abstract class

  • An abstract test should have test cases that cannot be overridden
  • It should also have an abstract Factory Method for creating instances of the class to be tested.

Abstract Test Rule 2

-Write a concrete test class for every implementation of the interface (or abstract class)

  • The concrete test class should extend the abstract test class and implement the factory method

Guideline

  • Tests defining the functionality of the interface belong in the abstract test class
  • Tests specific to an implementation belong in a concrete test class
    • We can add more test cases to TestSuperSlowStatPak that are specific to its implementation

Crash Test Dummy

  • Most software systems contain a large amount of error handling code
  • Sometimes, it is quite hard to create the situation that will cause the error
    • Example: Error creating a file because the file system is full
  • Solution: Fake it!