Critically important to the reliability of an application is how software components work together and how resilient they are to change. This article discusses how to perform functional testing on servers in the middle tier of distributed applications. We'll also address key middleware standards from the testing perspective, namely CORBA and Enterprise JavaBeans.
Testing plays a critical role in ensuring the reliability of software applications. To better understand what this means, we must consider the following questions:
Let's address the first question. Two methods of testing are used: functional and structural. The goal of the former is to prove that a software program conforms to its specification. Usually the tester reads the specification documents, creates a test plan and test data, applies the tests and checks whether the software behaves correctly. This method is commonly referred to as "black box" testing. Structural testing, on the other hand, uses the software program's source code for the creation of test cases. For example, you should ensure that all logical paths in the program code are executed at least once. This is known as "white box" testing. By looking at the source code, dependencies with other components can be determined and input data can be chosen accordingly.
- How is software tested?
- When is software tested during the different phases of a project?
Present-day software development often follows the object-oriented methodology. Here the waterfall model of "requirements...>design...>implementation...>test" has given way to a highly iterative and incremental development process.
This raises the second question: When is software tested? Software testing occurs at critical steps during the development and maintenance phases of a project:
As a project advances, the amount and importance of functional testing increases, while the amount and importance of structural testing decreases (see Figure 1). Structural testing is very important during the development of a software component. However, during integration the focus shifts to interfaces and the interactions between software components and modules, not to the software's source code. Furthermore, if a project is based on third-party components, the source code is typically not even available, rendering structural testing infeasible. When a project enters regression testing, the source code isn't consulted at all.
- Component testing locates errors within new software components during development.
- Integration testing identifies errors in interactions and interfaces of new, untested modules. This can occur during the development and maintenance phases.
- Regression testing occurs during the maintenance phase, ensuring that software continues to work after modifications are made.
Structural testing is limited to the definition and execution of tests by the software developer for a particular component's source code. (For Java classes it's also possible to perform structural testing to a limited extent without source code by analyzing the compiled Java and then generating test cases.) Far more important to the reliability of an application is how components work together and how resilient they are to change, hence the importance of functional testing.
Functional Testing of Nondistributed Applications
Before exploring how to test multitier applications, let's first look briefly at how nondistributed applications are tested. Tests can be performed at two levels: the user interface and the application programming interface.
At the UI level a tester simulates the end user, attempting to "break" the application. Many automated tools are available for recording and playing back user interactions with the UI. Although this method is popular with quality assurance departments, it's entirely black box, so the origin of errors is usually unknown.
At the API level a developer writes specialized test drivers. The objects or modules tested through the API must be linked with the drivers. If they don't have published interfaces, the developer usually performs the API-level testing.
Implications of Multitier Applications
Modern distributed applications are typically implemented in multiple tiers. Distribution has a significant impact on functional testing, as we'll see shortly. The three-tier architecture shown in Figure 2 has become quite pervasive, especially as applications continue to be enabled for Web access.
Within the three-tier architecture the first tier represents the presentation and interaction layer, such as a Web browser in a typical e-business application. The middle tier consists of the application logic, which can be constructed as business objects. These business objects may be new applications or existing ones that are encapsulated so they can be integrated into the environment. The third tier includes data repositories, such as relational or object-oriented databases.
Business objects, the most prominent part of the three-tier architecture, are the building blocks of a distributed application. As such, they can be located anywhere across a network and can be accessed transparently by client programs regardless of physical location. The underlying communication infrastructure is responsible for ensuring that a business object can be found and accessed.
Business objects also provide transparency to application developers by hiding networking and communication details. This allows an object to be implemented in the programming language that's most appropriate for a particular task. Ideally, method invocations can be made in the native programming language of the caller and automatically mapped into the language of the target object.
Performing functional tests on distributed applications requires a significant amount of API-level testing, which is considerably more complex in comparison to testing nondistributed applications. A typical distributed application comprises many application modules that span different operating systems and network connections. There are multiple participants in the development and testing of a distributed application, which makes coordination a challenge. In the Internet age, changes to software can occur frequently with very short delivery cycles. Furthermore, changes to distributed applications, such as those supporting e-commerce on the Web, must be introduced incrementally without bringing down the entire system.
All of this complexity poses a dilemma for the tester. Which part of the application has failed? Who's responsible - the UI developer, business object developer or database administrator? What's going on within the business object? Using standard middleware can greatly alleviate this situation, as evidenced by the growing use of CORBA as the communication backplane for business objects, and Enterprise JavaBeans as a standard for server application components.
Standard Middleware for Objects
Middleware provides a means for integrating business objects in the middle tier (see Figure 3). CORBA is a pro-ven middleware standard for enabling object-level communication in multitier, multilanguage systems. From an application perspective, CORBA provides tremendous flexibility, but may present an abstraction level that's too low for some projects. In other words, the object paradigm supported by CORBA forces the programmer to define the application framework in addition to the business objects. (Note: The recently adopted CORBA Component Model should raise CORBA's abstraction level to be comparable with Enterprise JavaBeans. Please read on.)
The EJB specification is another middleware standard. It's much newer than CORBA, but is evolving - that is, maturing technically and gaining market acceptance - rapidly. EJB is a component paradigm that has gained recent prominence with the advent of application servers for three-tier development. In the EJB model a programmer writes code to implement specific business objects. A standard container supplied by someone else handles the rest. All requests from outside the programmer's component are directed to the container, which is responsible for executing the correct code within the object implementation. The container and the object communicate through a protocol that is well defined by the EJB specification.
EJB can be thought of as an abstraction layer that can exist on top of CORBA. So while standard middleware eliminates some of the uncertainty within a distributed environment, having multiple standards may also require testing at different levels.
Testing Middle-Tier Servers
Standard middleware provides a convenient way to develop business objects, but is it possible to perform functional testing of objects independent of an application user interface? Fortunately, the answer to this question is Yes.
The basic approach to functionally testing business objects is to substitute a generic test tool in the "Presentation & Interaction" tier (see Figure 3) that can work directly with the business objects. In other words, impersonate the expected caller of the server. Such a test tool must address the following three challenges:
Standard middleware, such as CORBA and EJB, makes it possible to implement a generic test tool (see Figure 4). This is achieved by meeting the challenges of obtaining interface details, constructing and executing method invocations, and recording test sessions.
- How to obtain information about object interfaces: To solve this first challenge, published interface descriptions can be accessed through mechanisms provided by standard middleware. For example, CORBA provides the Interface Repository, which contains interface descriptions that can be accessed at runtime. EJB utilizes JNDI for locating objects and metadata descriptions for home and remote interfaces, bean type and business methods. A generic test tool would have a user interface through which information about a business object's interface could be presented. Details about an interface include available methods, method parameters, return values and possible exceptions.
- How to invoke methods: In a generic test tool it would be impractical to require compilation of static object descriptions into the tool. Instead, dynamic invocation should be used to create method calls as needed by the tester. Fortunately, CORBA's dynamic invocation interface (DII) and Java's reflection API support dynamic discovery of interfaces and the creation of method calls on the fly. The test tool's usage model must allow selection of a method to test, visual creation and editing of arguments, and immediate viewing of results and exceptions.
- How to record and replay: Requests and replies made through a tool should be saved using a suitable external data representation, such as XML. The user should be allowed to decide which actions to record and save for future use. The test tool would then interpret the external data to replay a session. The external data could also be used to generate standalone test clients. These capabilities are required to support regression testing.
When considering how to perform funtional tests on middle-tier servers, look for the following:
- Time and money savings: A good test tool should eliminate the need to build custom test programs and to manually create regression test cases.
- Investment protection: Test tools and procedures should be built on open industry standards so that technology investments are protected.
- Productivity improvement: Test procedures should be clear and intuitive. Test automation tools should offer ease of use.
- Successful deployment: All server components within a distributed system should be properly tested, thereby increasing your confidence that the application will work in production.
Todd Scallan is the director of product management for Segue Software's distributed computing products. He holds a BS in electrical engineering from Lehigh and an MS in computer engineering from Syracuse.
He can be reached at: [email protected].
Thomas Kern has 12 years experience in systems programming and application
development for UNIX and NT. He is coauthor of a book about programming the
X Window System and Motif.
He can be reached at: [email protected].