Part II Approach
7.2 Design Dependencies
7.2.1 Hard-Wired Dependency
Motivation Changing the supplier
class
During unit testing, a tester often wants to redefine implementa- tion details of a supplier class in order to gain more control over the client class (the CUT), the supplier class, and other, indirect supplier classes during test setup and execution. For testing purposes a tester may want, for example,
• to predefine the return value delivered by a method of the supplier class to enforce a specific execution path of a client method,
• to define the value of a non-private attribute of the supplier instance to enforce a specific state (of the supplier instance) as required for test setup, or
• to break dependencies of the supplier class to other indirect supplier classes of the CUT in order to simplify testing. Changing the CUT Changing or substituting the supplier class for the sake of testing
is not always possible without changing the source code of the
1 The navigability of class associations should be specified during the de- sign activity. If the navigability is however not specified, we assume that it is supported in both directions of the association.
Chapter 7: Design 63
CUT. Changing the CUT for testing purposes, however, should be avoided because it breaks the consistency between the CUT and its final, operational version and therefore reduces the sig- nificance of the test results. Another reason why creating differ- ent versions of classes for testing purposes is undesired is its negative (i.e. increasing) effect on the administration effort and the danger that a wrong class version is integrated into the final product.
Hard-wired dependencies are the reason why details of the sup- plier classes can’t be redefined without changing the CUT and therefore cause the greatest restrictions on testing a CUT in iso- lation.
Definition
Hard-wired dependency A dependency from a client class to a supplier class is hard-
wired,
1 if it is impossible to redefine1 any implementation detail2 of the supplier class and
2 if it is impossible to define3 the value of any non-private attribute of the supplier class
the client class references, without changing the implementation of the client class (or any of its superclasses) or the supplier class.
Identifying hard-wired dependencies
To identify hard-wired dependencies, search for dependencies where at least one of the following four facts holds:
1 The supplier class is a (direct or indirect) superclass of the client class.
In this case, it is impossible to substitute the superclass with- out changing the source code of the client class4.
2 The client class creates an instance of the supplier class directly.
1 To redefine an implementation detail means to overwrite a method used by the client class or to substitute the supplier class.
2 An implementation detail is a method, a static initializer or instance initial- izer (see Appendix B.4.1 and Appendix B.4.2).
3 Same as “to set an arbitrary value”.
4 Alternatively, the source code of one of the intermediate superclasses could be changed as well to break the dependency to the indirect super- class.
64 Chapter 7: Design
In Java, a client class creates an instance of the supplier class directly by calling one of its constructors like
new Sup-
plierClass()
. In this case, it is impossible to substitutethe supplier class without changing the source code of the client class.
A client class may also create an instance of a supplier class by calling a pseudo-constructor, i.e. a static operation which returns a new instance of the supplier class. Examples of pseudo-constructors are the method
getInstance()
of the Singleton-pattern [Gamm94] and the methoderzeuge()
of the persistency framework described in [Six03].As a heuristic to identify pseudo-constructors of a class
A
we search for static methods with a return value type– equal to
A
or– equal to an interface implemented by
A
.Note: A common practice is not to model the specific way of object creation during the design activity. This practice ignores, that object creation contributes signifi- cantly to the number of hard-wired dependencies within a system.
3 All methods of the supplier class accessed by instances of the client class can’t be overridden and all attributes of the supplier class accessed by the client class or its instances may never have its value set.
In the context of Java this means, that all methods of the supplier class called by the client class (or its instances) are static or final and all attributes accessed by the client class are final.
Notes:
– Whether a method, which is not called by the client class, is static or final is not relevant. Whether an attribute, which is not accessed by the client is final, is not relevant, too.
– Final methods and attributes are not part of the UML. However, if final declarations are used during implemen- tation, then they should be treated as a design issue and modeled in class diagram e.g. with help of UML stereo- types.
– During design, method calls are seldom modeled in detail, e.g. using interaction diagrams. Still it is possible to identify hard-wired dependencies in some cases, if e.g. the supplier class contains only static methods or if
Chapter 7: Design 65
the client class is intended to access only a specific sub- set of methods (of the supplier class) which are all static. 4 it is impossible to create a subclass of the supplier class.
In Java, a class can’t be subclassed if it is declared to be final.
Note: Final classes are not part of the UML. However, if final classes are used during implementation, then this should be treated as a design issue.
If a client class depends on a supplier class which can’t be subclassed, there is no way to use a subclass of the supplier class to substitute it.