How to use Rational XDE to create a pattern for JUnit.
     
 
     
 

JUnit Test Patterns in Rational XDE

By Frank Hagenson

 

This article first appeared in The Rational Edge.

 

Unit testing is one of the most important techniques available in the development of quality software. When you start to write unit tests however, itís not long before you realize just how much effort is involved. Fortunately, a good unit-testing framework can really help, and one such framework is JUnit, which was originally developed by Kent Beck as part of his eXtreme Programming methodology.

 

Even with a good framework, however, you must still write a lot of repetitive and mundane code just to support your test harness. In this article, I show how I used Rationalģ XDEô to create a design pattern that automates the creation of test cases and suites, using the JUnit framework.

 

The goals of this project were:

1)       Automate the generation of all housekeeping code. I wanted my pattern to generate all required structural and behavioral code that doesnít needhuman input (i.e., doesnít require thinking).

2)       To make the generated code compile and run even when the pattern is applied multiple times.

 

This was the first iteration of my pattern, so to keep the scope manageable I restricted the pattern to working with one class at a time. I also did notdo anything fancy, like trying to keep the test cases in sync with the application code. If a class name is changed, the test code will break, and itís a manual job to fix it.

JUnit

JUnit is a test framework. It provides a set of classes and tools that can be used to create and run tests for Java applications. The core of the JUnit framework is the TestCase class, whichprovides basic functionality to create and run tests. The normal usage scenario is to create a sub-class of TestCase, and then add methods to it that test something.

 

In order to run the tests, the new TestCase is added to a TestSuite, and the method TestSuite.run is called. The TestSuite then uses reflection to run the test methods of the TestCase. Any method in the TestCase that begins with ďtestĒ is treated as a test case and executed. So you use Java code to test Java code.

The Test Harness

The test harness is all the code that is there simply to do testing. It adds nothing to the functionality of the released application. Any piece of code that I want to test is called the ďUnit Under Test,Ē or UUT for short. With Java, the UUT is generally a class, because in Java the class is the smallest unit of stand-alone code.

 

The test harness code can easily take up as much code as the application under test itself, so it makes good sense to structure the harness in a way that makes it easy to manage and modify. The best way I have found to do this is to create a subclass of TestCase for each UUT, and a subclass of TestSuite that allows you to run all test cases in one go. Each TestCase will invoke methods on the UUT and check that the values returned are as expected.

 

For example, if I had two classes, Adder and Subtractor, I would create two test cases called TestAdder and TestSubtractor, respectively, plus a test suite called something like AllTests. The UML representation of the test harness structure would look something like Figure 1.

 

Figure 1: UML Class Diagram of Test Harness

 

To make it simple to test a single class, I normally add a main method to the TestCase that simply runs the tests in the class and outputs the results to standard out. In this way I can just compile and run the test case to check whether the code Iíve just developed works or not. The JUnit framework provides everything I need to do this, so the main method for a class like TestAdder would look something like this:

 

†† public static void main(String[] args) {

††††† junit.textui.TestRunner.run(AdderTest.class);

†† }

 

I also put a main method on the TestSuite class, so that I can easily run all my tests from the command line. I generally make running this TestSuite a part of the build process, so that no application is said to build correctly until it has compiled and all its test cases have executed successfully.

 

The main method of the test suite would look something like this:

 

†† public static void main(String[] args) {

††††† AllTests suite = new AllTests();

††††† junit.textui.TestRunner.run(suite);

†† }

 

 

The TestSuite constructor also needs to do a bit of work. It must add all known TestCases to the suite, so that the main method can execute them. So my test suite constructor would look something like this:

 

†† public AllTests() {

††††† this.addTest(new TestSuite(TestAdder.class));

††††† this.addTest(new TestSuite(TestSubtractor.class));

†† }

 

 

Then I can finally start adding test methods to the TestCases to exercise the behavior of the classes being tested. Supposing my Adder class had a method named add that returned the sum of two numbers passed to it; a test for this method might look something like this:

 

 

public void testAdd()

{

††† Adder uut = new Adder();

††† assertTrue(ď1+1!=2Ē, uut.add(1,1) == 2);

}

 

This method instantiates a new Adder and checks that the result of the call to add equals two by calling the JUnit framework method assertTrue. If the second parameter passed to add does not evaluate to true, JUnit throws an exception and records this as a test failure.

 

Only the code in the testAdd method is really specific to testing the Adder class. So even with a framework like JUnit, there is a lot of housekeeping code that needs to go in place before I can actually write the tests.Another painful thing is that every time I create a new test case, I have to go and add it to my TestSuite. These are the kinds of tasks Iíd like to automate.

 

In summary, the tasks to be automated are:

1.       Write the structural code for a new TestCase.

2.       Write the TestSuite code if we donít have one yet.

3.       Add the code to add an instance of the new TestCase to the TestSuite at run time.

4.       Write a main method for the new TestCase that runs the TestCase and outputs the results to standard out.

5.       Write a main method for the TestSuite that runs all TestCases and outputs the results to standard out.

The Structural Pattern

I decided to put the pattern and the JUnit framework classes together in a model, which can be reused with any application Iím developing. That way, I can just open the model and apply the pattern as required. All I have to remember to do is add a reference from my application model to my JUnit pattern model, so that the code will generate and compile correctly.

 

First, I must reverse engineer the JUnit framework, to get access to the classes it contains for my pattern. To do this, I create a new Java Modeling Project named JUnit, and copy the JUnit framework archive, junit.jar, into the root folder of the project. In the model navigator, I right click on the model and select from the menu:

 

††††††††††††††† More Java Actions | Add / Remove Modeled FilesÖ

 

In the Add/Remove Modeled Files dialogshown in Figure 2, I set the File Types combo to ď*.jarĒ and click the Add Recursively button. XDE pulls junit.jar into the project. When I click the OK button, XDE reverse engineers all of the classes in the .jar file.

 

 

Figure 2: Reverse Engineering JUnit

 

Abstracting out a general design pattern from the main usage scenario above, I come up with two parameters for our pattern. The first is the class I want to test, and the second is the suite, which may or may not exist.

 

In Rational XDE, creating a new design pattern is easy. I just right click on the JUnit model in the Model Explorer and select

 

††††††††††††††† Add UML | Pattern Asset

 

from the menu. XDE presents me with a dialog box to specify how I want the pattern asset created, as in Figure 3. Rational XDE will create a new package with the stereotype <<Asset>>. Inside that will be a new Parametized Collaboration.

 

 

 

Figure 3: Creating a Pattern Asset

 

The Parametized Collaboration is a UML element I can use to specify any collaboration of UML elements that will modify or use a set of input parameters in some way. In this case, the input parameters are a class to be tested, and a test suite. The pattern will use these input parameters to generate the structure of a test harness. I add these parameters to the Collaboration by right clicking on it in the Model Explorer and selecting

 

††††††††††††††† Add UML | Template Parameter

 

from the menu. To specify that the input parameters are classes, I then select each one, right click on it, and select

 

††††††††††††††† Add UML | Type | Class

 

from the menu. This gives me a structure, as shown in Figure 4.

 

 

Figure 4: Parametized Collaboration Structure in the Model Explorer

 

Creating the structural aspect of the design pattern is straightforward. I simply create a new class diagram in the <<Asset>> package and put some classes on it with relationships between them. In this case, I need to add the TestCase and TestSuite classes from the JUnit framework, the classes that are parameters to the collaboration, any classes that will be generated as part of the pattern expansion, and then the required relationships between them. So my initial design looks like the class diagram in Figure 5. Now, when I apply the pattern, XDE prompts me for the class to be tested and the TestSuite, and then adds any other classes in the diagram to my model, including any relationships I have specified between these classes.


 

 

Figure 5: Initial Structural Aspect of Pattern

 

 

 

But hereís where I have a problem. Iíve specified that the pattern should create a sub class of TestCase called MyTestCase, to be the test case for the UUT. However, that name is not very descriptive, and if I apply the pattern twice, Iíll get two classes called MyTestCase. What I really want is to pre-pend ďTestĒ to the name of the class Iím trying to test, so that my test case for Adder would be TestAdder, for example.

 

Luckily, XDE provides this capability through the use of scriptlets. A scriptlet is a little piece of JavaScript that will be executed when the pattern is applied. To tell the pattern engine to execute the scriptlet, I just need to enclose it in <%Ö%> markers. To tell the pattern engine to use the output of the execution as a replacement string, I just include a= (equals symbol) as the first character in the execution.Notice that this is very similar to the use of JavaScript in an ASP or JSP page.

 

I make this change and apply the pattern to a class. The pattern expansion creates a new test case and test suite class, and also a class diagram showing the expansion in the target package, as expected. Unfortunately, it also creates a copy of the framework classes in the destination package, but what I really want is for the expanded classes to reference the framework classes in the JUnit model.

 

To rectify this situation, I need to modify the merge behavior of the pattern via the Pattern Explorer. When I select the class diagram in the Pattern Explorer, I can view its Pattern Properties, one of which is the merge behavior. The different ways a UML element in a pattern can be merged into a model during pattern expansion are described in Table 1.

 

Table 1: Merge Behaviors for Pattern Elements

 

Value

Meaning

Merge

Create a new element if it doesnít exist; update if itís already there.

Preserve

Create a new element if it doesnít exist; donít touchif itís already there.

Replace

Always create a new element; overwrite if itís already there.

No Copy

Never create a new element; never update if itís already there.

 

 

By changing the merge behavior of the diagram from ďReplaceĒ (the default) to ďNo Copy,Ē the diagram is not created in the model at expansion time; and, as a side effect, the framework classes that are present on the diagram are not re-created in the model when the pattern is expanded.

Adding Behavior

Okay. Now I have a pattern that will create the structural aspect of a test-harness architecture. But architecture is more than just structure. What about behavior?

 

The actual tests that I will write to test my code will be different for each class, so they are not good candidates to automate as part of the pattern. But there are a lot of things I need to do for my test cases and suites that stay the same each time. For example, I always add a main method to each test case and the suite, and these main methods do pretty much the same things every time, the only difference being the class theyíre operating on.

 

XDE provides me with a way to automate this kind of behavioral code through the use of code templates. With a code template, I can bind a piece of Java code to an operation on a class. When I apply the pattern, the Java code is squirted into the implementation body for that class. Whatís even better is that I can use scriptlets in these code templates, and I can specify parameters to pass to the scriptlet when it is expanded.

 

For example, for my TestCase, I add a main method, and then right click on that method in the model explorer and select

Code Templates | Bind

 

from the menu to bind a new code template to the method. The code template editor appears, and I can specify any existing model elements to use as parameters to the code template expansion. In this case, I want the code template to run the TestCase, so I specify the TestCase as a parameter to the code template. This can be seen in the Code Template Editor in Figure 6. I specify that a parameter named ďTestCaseĒ will be passed to the code template expansion, and that its value is expanded from the scriptlet Test<%=UUT%>, which is the name of the new TestCase the pattern creates.

 

 

Figure 6: The Code Template Editor

 

Inside the code template body, I use scriptlets to access these parameters. So the code template I bind to the main method of my TestCase looks like this:

 

††††† junit.textui.TestRunner.run(<%=TestCase%>.class);

 

When the pattern is applied to the Adder class, this will expand to:

 

††††† junit.textui.TestRunner.run(TestAdder.class);

 

which is exactly what I want.

 

When I bind a code template to an operation, XDE gives me the option of specifying if it is a one-time expansion or not. If I select one-time expansion, then the code is generated, and I can modify it at will. If I donít select one-time expansion, the code will be generated with a couple of marker comments around it. Then, any code inside those marker comments is overwritten by XDE whenever the code and model are synchronized.

 

In my pattern, Iíve made use of both ways of binding templates. For my main method, Iím not anticipating any customizations to the code, so I havenít made one-time expansions. Any changes made inadvertently will be cleaned up next time I synchronize.

 

Iíve also automated the addition of the TestCases to the TestSuite. First, I define a method called addAllTests in the TestSuite and bind a code template to the constructor of the TestSuite that calls this method. Then, I bind the following piece of code to the addAllTests method:

 

††††† this.addTest(new TestSuite(<%=TestCase%>.class));

 

 

 

As you can see in Figure 6, Iíve set the parameter TestCase to be Test<%=UUT%> so it expands to the name of the TestCase class thatís just been generated, and binds the resulting piece of code to the addAllTests method.Iím anticipating that this method might be customized, so Iíve bound the code template as a one-time expansion.

 

Just to provide the mandatory scope creep, Iíll also automate the instantiation of the UUT. In my usage scenario above, I explicitly create a new Adder just before I test it. With my pattern, I add a method to do that for me; JUnit allows you to provide a method on your test case named setUp, which it will execute before it calls each testÖ method. Iíve added the method and bound the following code template to it:

 

††† mUut = new <%=UUT%>();

 

This expands to calling the default constructor for the UUT and storing the object in a data member named mUut.

 

JUnit also allows you to provide a tearDown method, which it calls immediately after calling each testÖ method. To this method, Iíve bound the following code template:

 

††† mUut = null;

 

To support these two method bodies, I change the dependency between the test case and the UUT into a private aggregation, with the role name mUut. XDE generates a field reference for this. I can use the generated setUp and tearDown methods as starting points or leave them as is.

 

The last thing Iíll add is a default test operation named testDoSomething, and bind a code template to it that simply throws a failure back to the JUnit framework when executed. This is useful, because it doesnít mislead me into thinking Iíve got a test for something when I havenít. When I implement the test, I can replace the code that throws the failure with a valid test.

Merging the Pattern

So now we have a pattern that creates both the structure and behavior of a test harness. We have satisfied the first goal of the project: to generate all housekeeping code automatically. Unfortunately, the second goal requires a bit more work.

 

If the pattern is expanded at this point, the resulting code wonít compile. The generated TestSuite is missing import statements for the JUnit framework classes it requires; Test and TestResult. I could add dependencies to both of these classes, but I really want the TestSuite to have access to all classes in the junit.framework package, as other classes may be needed when I start coding inside the TestSuite. I really want the statement

 

††††† import junit.framework.*

 

in the TestSuite class. To do this I create an <<access>>dependency between the Java component the test suite resides in and the junit.framework package. The resulting class diagram is shown in Figure 7.

 

 

Figure 7: The Final Structural Pattern

 

 

Now the generated code will compile and run, although the test case will fail with a ďNot implementedĒ message. But as soon as the pattern is applied to a second class using the same TestSuite, the first TestCase is no longer executed. Whatís happened? The addAllTests method of the TestSuite has stopped working. It now only adds the last TestCase created to the TestSuite. The second application of the pattern has overwritten the existing method definition with its own definition. What I really want is for the body of the second code template to be appended, so that both TestCases are added. I do this, by delving into the merge options for the pattern again.

 

By default, the merge value for the addAllTests operation is set to ďReplace,Ē so if there is an existing element in the target model with the same name, it is overwritten by the element from the pattern application. This is why the body of addAllTests gets replaced when the pattern is applied to a suite the second time. By changing the merge value to ďMergeĒ, the code templates bound to addAllTests will actually be merged into one method body, so all test cases will be added when the method is called.

 

To help organize my model, I want to put test cases and suites into a package separate from the real application code. It is easiest to place them into a subpackage of the package where the UUT resides. Iíll name this subpackage test, so Iíll know what is in there.

 

To do this, I need to open the pattern in the Pattern Explorer again, and go to the

 

††††††††††††††† Advanced Properties | Default Expansion Location

 

property.I change the property to expand to the package of the template parameter UUT, and append ďtestĒ to that, as shown in Figure 8.

 

Figure 8: Setting the Default Expansion Location for the Pattern.

 

The last tweak Iíll make is to give the user the option of either selecting an existing TestSuite class from the model, or providing a name to generate a new one. To do this, I need to set the pattern properties for the Suite template parameter. So in the Pattern Explorer, I browse to the

 

Suite | Advanced Properties | Value Sources

 

property, and change the value source. The possible values for this are summarized in Table 2. The value I want to set it to is ďUser or Generated,Ē which gives me the option of selecting an existing model element or entering the name for a new TestSuite.

 

Table 2: Value Sources for Pattern Template Parameters

 

Value

Meaning

User

Parameter must be selected from model.

Generated

Parameter must be typed as a string.

Collection

Parameter must be a collection of an element selected from model.

User or Generated

Parameter may either be selected from the model or typed in.

User or Collection

Parameter may either be an element selected from model or a collection selected from model.

Generated or Collection

Parameter may either be typed in or a collection of an element selected from model

Any

Parameter can be entered in any one of the three ways

 

 

 

By default, XDE would still prompt me for the expansion location and default it to the value I have specified here. To make my life easier, Iíve also specified that the prompt for this value and the bindings location should be suppressed. To do this, I browse to the

 

††††††††††††††† Advanced Properties | Application Wizard Properties

 

node in the Pattern explorer and check both the Suppress Bind Location Dialog and Suppress Expand Location in Dialog checkboxes.

The Final Product

Finally, the acid test. The first time I apply the pattern, I am presented with the dialog shown in Figure 9.

 

Figure 9: Selecting a Unit Under Test (UUT)

 

I select the class I want to test as the UUT, and click next. But when prompted for the suite, there is no existing class in my model that is a TestSuite, so I select the generated value option, enter the name of my new TestSuite, and click the Add button, as shown in Figure 10.

Figure 10: Specifying a New Test Suite

 

Mypattern generates a TestCase called TestAdder, and a TestSuite called AllTests. When I compile and run AllTests, I get one failure because the test testDoSomething has not being implemented yet. So far so good.

 

When I generate a TestCase for Subtractor, I select the TestSuite Iíve just created. When I compile and run the TestSuite AllTests, two tests are executed. Success! Now that XDE has done the donkeywork, I can go and add some real test methods to my two new TestCases.

Next Steps

My pattern deals with only one simple but common use of the JUnit framework. There are other common uses that could be implemented fairly easily - for example, creating a TestCase as a nested class of the UUT. Also, there are many places this particular pattern could go. Selecting a package and generating TestCases for all the classes inside is one suggested enhancement. Generating a default test method for each public method of the UUT is another.

 

All of these options are outside the scope of my project, however, so they will have to wait for another article. Meanwhile, my project did create a small but useful pattern and afforded a good dig around the XDE pattern engine in the process. Keep in mind that the pattern engine is extremely powerful, and Iíve really only scratched the surface of its potential. I hope this article will serve as an introduction to what is possible and set your creative juices flowing.

References

To get more information about JUnit and download the latest version of the framework, visit http://www.junit.org.

For information on eXtreme programming, visit http://www.extremeprogramming.org or http://www.xp.co.nz.

Acknowledgements

My deepest thanks to Yves Holvoet for his help in producing both this article and the XDE model it is based on.

 

 
 

Free software and source code for download!

Create instruction manuals easily