Welcome!

Eclipse Authors: RealWire News Distribution, Adam Blum, Aditya Banerjee, Jeff Anders, CJ Fearnley

Related Topics: Java

Java: Article

Using Aspects to Unit-Test the Integration of Third-Party Components

Creating proxies to exercise the branches of our code

To generate the Web Service client code I invoked the wsdl2java tool that comes with the axis2 download. The invocation I used is:

wsdl2java -uri http://www.Webservicex.net/globalweather.asmx?wsdl -o c:\WeatherProject\src c:\commodities\trunk\java\src -p weather.ws

Wsdl2java generates GlobalWeatherStub Class that is used to communicate with the Web Service.

Listing 2 shows the JUnit tests that are used to validate that the Weather object is working as designed. As you can see all the tests are pretty straightforward and they test the basic functionality of the Weather class. Unfortunately not all the functionality has been validated given the current set of test cases. The testInvalidCity() and testInvalidCountry() methods are incomplete. The both validate that the WeatherException is thrown but they don't test whether or not the correct Log4j log message was generated. How do you test such a third-party library that stores no state of its own? To test this I create an aspect called LoggerAspect (see Listing 3).

LoggerAspect is annotated with @Aspect which indicates that this is not a class but an aspect. The next important part of this aspect is the @Pointcut. The @Pointcut indicates the classes and/or methods that the aspect crosscuts. In this case it's the execution of anything that matches classes that are in the org.apache.log4j package and call either the info(), warn(), or error() methods. If during execution such a class and method are encountered doLogger() is invoked. The doLogger() method contains the crosscutting functionality. The @Around(doLogger()) annotation indicates the type of advice performed when the join point is encountered. In this case it is @Around advice. @Around advice tells the aspect to execute the doLogger() method both before and after the intercepted method is executed. doLogger() takes an argument of type ProceedingJoinPoint. ProceedingJionPoint contains methods that let you check the arguments sent into the crosscut method as well as methods that let you execute the original crosscut method (ProceedingJoinPoint.proceed()). Therefore you can execute some code, call ProceedingJoinPoint.proceed(), and then execute some additional code. The doLogger() method checks the arguments passed to the info(), warn(), or error() method and if they are of type String, the message is stored in the static data member messageList that is a List<String>. Now anytime a message is logged it is added to the list whose contents can be examined at a later time. Listing 4 shows the revised testInvalidCity() and testInvalidCountry() codes incorporating the use of LoggerAspect. Notice that now the log messages can be tested and verified.

Our test cases are getting better but they are still lacking. Because we are using a third-party Web Service we cannot control the return result. In Listing 5 the WeatherData class throws an exception if the <Status> XML element does not equal "Success." How is such a condition tested? We do so by creating an aspect that crosscuts the call to GlobalWeatherStub.GetWeather() and returns a result where <Status> does not equal "Success." GetWeatherAspect is shown in Listing 6. This aspect creates a join point during the execution of weather.ws.GlobalWeatherStub.GetWeather and calls doGetWeatherOverride(). Again we use @Around advice for the implementation of doGetWeatherOverride(), which simply returns a GlobalWeatherStub.GetWeatherResponse that contains the XML string provided in the xmlResult data member. Listing 7 contains the code for a test case that makes use of GetWeatherAspect. GetWeatherAspect.xmlResult is set equal to an XML String where the <Status> node is set to failure. The test verifies that a WeatherDataException is thrown from WeatherData upon encountering the status of "Failure".

Now that the aspects have been created and the tests have been modified to make use of them, some configuration needs to be done for the aspects to be correctly woven during runtime.

The first thing that needs to be done is to create an aop.xml file (see Listing 8) in a META-INF directory. The META-INF directory needs to be added to your classpath. The aop.xml file is comprised of two parts. The first section, <weaver>, describes which object the Aspects will be woven into. This description can be wild-carded and can be as narrow or broad as is needed. The second part, <aspects>, lists all the aspects being woven. In our aop.xml the two aspects, LoggerAsepct and GetWeatherAspect, are woven into all classes under the packages org.apache.log4j and weather.ws. Finally you must instruct the JVM to use the aspect Weaver when executing. This is done by specifying javaagent:<aspectjweaver file location>\aspectjweaver.jar on the command line.

Conclusion
The use of runtime aspects facilitates the testing of the interaction between of third-party components and the application embedding them. It is not always possible to control how the third-party components respond, which makes testing all possible interactions with them difficult. Aspects give us a way to proxy third-party components and capture data or produce responses that exercise the branches of our code. The use of runtime aspects does so in a way that doesn't bloat our code with test-only components. Their use also maintains the original program flow of the application that is released to a production environment. With applications becoming more and more complicated with more and more interactions with other systems, good thorough unit testing is more vital than ever. The use of aspects gives us one more tool to help us test all branches of our application.

More Stories By Mark Nadelson

Mark Nadelson has been working as a software developer for the past 18 years in a variety of industries including telecommunications, Internet, and finance. He has published numerous articles as well as two books. He enjoys exploring new technologies and new techniques to solve complex problems.

Comments (1) View Comments

Share your thoughts on this story.

Add your comment
You must be signed in to add a comment. Sign-in | Register

In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.


Most Recent Comments
immohuneke 01/06/09 08:14:24 AM EST

A well written article, an ingenious solution to a real problem often encountered in testing and a neat answer to those who claim that AOP is just a solution looking for a problem. However, I found only the first five listings in the file "Nadelson_source.rtf", which makes the last part of the article harder to understand. Please could you check whether it's all there?