A Framework for Testing Object-Oriented Components

The terms “component” and “component-based software engineering” are relatively recent. Although there is broad agreement on the meaning of these terms, different authors have sometimes used slightly different interpretations. Following Brown and Wellnau [4], here we view a component as “a replaceable software unit with a set of contractually-specified interfaces and explicit context dependencies only.” A component is a program or a set of programs developed by one organization and deployed by one or more other organizations, possibly in different application domains. To date, many component systems are designed and developed using object technologies. The same is true of middleware architectures that support the deployment of components, such as Java Beans [3], CORBA [1], and DCOM [5]. Although there is consensus that the issues in component-based software engineering cannot be solved merely by object technologies [4, 14], these technologies will clearly play a fundamental role in the production of software components [15]. It is quite likely that developers will use with increasing frequency object technologies in the design and implementation of components. For this reason, in the sequel we focus on testing of components that consist of an object or a set of cooperating objects. In addition, we consider messages sent to a component as method invocations on the objects contained in the component. Programs developed with object technologies have unique features that often render traditional testing techniques inadequate. An important feature of these programs is that the behavior of a method may depend on the state of the method’s receiver. Of course, the state of an object at a given time reflects the sequence of messages received by the object up to that time. As a result, erroneous behaviors may only be revealed by exercising specific sequences of messages. Component testing should identify sequences of messages whose execution is likely to show the existence of defects; however, traditional unit and integration testing techniques do not produce this kind of information [2] Our method uses various kinds of structural analyses in order to produce sequences of method invocations for the component under test. First, we rely on data-flow analysis in order to identify paths of interest in the component under test. Next, we apply a combination of symbolic execution and automated deduction techniques in effort to generate sequences of method invocations that exercise the paths identified earlier. A byproduct of our testing method is the definition of formal specifications in the form of preconditions and postconditions for the methods contained in the component under test. When performing integration testing, the method sequences resulting from the analysis of single components are then used jointly. Evidently, developers and users of a component have different needs and expectations from testing activities. Our framework for component testing can benefit both parties involved. On the one hand, the method sequences we identify can be used as test cases to be applied during the development of the component under test. On the other hand, during integration testing component users can combine