Monday, June 4, 2012

JUnit Test Patterns

http://www.abracadabrasolutions.com/JUnitArticle.htm 
 
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 need  human 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 not  do 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, which  provides 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 dialog  shown 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 touch  if 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


My  pattern 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.

No comments:

Post a Comment