TOPL (Testing Objects Pattern Language)
Author: Donald Firesmith
Although there is always a considerable risk associated with making
predictions, I am convinced that object-oriented patterns will be viewed as the
most important concept in the object community during the 1990s. For example,
within a single year of its publication, the book Design Patterns [Gamma et al.,
1994] has been recognized as the number one book in the list of top ten all-time
classics in the object technology field [Bilow, 1995]
According to the Dictionary of Object Technology [Firesmith and Eykholt,
1995], a pattern is "any reusable architecture that experience has shown to
solve a common problem in a specific context." A pattern language is a
consistent collection of related patterns gathered together to deal with a
specific problem domain. Several important object-oriented testing techniques
have been captured in the form of test patterns. These patterns have been
collected together to form PLOOT, the Pattern Language for Object-Oriented
Testing, which has been developed specifically to deal with the following
general issues that differentiate object-oriented testing from traditional
software testing:
- Unit testing is the testing of classes, typically indirectly by means of
their objects. This is because (1) classes are the basic units of design, (2)
classes are usually source code entities whereas objects are run-time entities
that may be executed, and (3) individual operations within the class can not
be tested in isolation because they depend on the environment of the class
(e.g., other operations, properties, exceptions, assertions, etc.).
- Objects are essentially software blackboxes that hide their encapsulated
properties (e.g., attributes, links, component parts) and hidden operations
behind an interface of visible operations.
- Objects may exhibit externally-visible state behavior (e.g., a pop message
sent to an empty stack raises a stack empty exception rather than returning
the top entry).
- Objects collaborate via messages and exceptions, whereby messages may be
dynamically bound to polymorphic operations. A unit test case is therefore
typically a test message (with or without arguments) sent to an object under
test that is in a specific state rather than test data sent to a function.
- Objects are defined by means of classes which are related via inheritance
relationships.
- Integration testing must take into account collaboration, inheritance,
aggregation, and attribution as well as patterns and mechanisms.
- An iterative, incremental, parallel development cycle is often used in
which testing is performed incrementally, must be repeated often, and is begun
earlier in the project schedule.
This chapter has presented the initial version of PLOOT, a pattern language
for the testing of object-oriented software. Future versions of PLOOT will add
additional patterns, especially in the area of integration testing, to the
following current patterns:
- Built-in Test Operations. This is a pattern for automating the complete
whitebox unit self-testing of classes while bypassing encapsulation.
- Mixin and Join Test Hierarchies. This is a pattern for automating the
complete whitebox unit testing of class using mixin and join classes to bypass
encapsulation.
- Friends Test Hierarchy. This is a pattern for automating the complete
whitebox unit testing of class using friends to bypass encapsulation.
- Separate Test Driver Hierarchy. This is a pattern for automating the
complete blackbox unit testing of class.
- Paired Message Sequences. This is a pattern for performing state-based
blackbox unit testing of classes specified via axioms in terms of pairs of
message sequences that should transition equal objects into equal objects.
- Transition Trees. This is a pattern for performing state-based blackbox
unit testing of classes in terms of transition trees base on state transition
diagrams.
- Operations that Delegate. This is a pattern for testing operations that
delegate responsibilities to other classes
- Dependency-Based Testing. This is a pattern that determines the optimal
test order for classes based on dependency relationships among the objects.
- Assertions and Exceptions. This is a pattern that supports class level
debugging by embedding assertions and exceptions in the classes under test
- Use Case Testing. This is a pattern for the acceptance testing of reactive
applications with user interfaces in terms of use cases.