JDemo: Interactive Testing RefactoredToday's software development is unthinkable without automated testing; e.g., writing unit tests using the JUnit framework. But there still are some aspects that cannot be tested automatically. An example is the appearance of user interface components:
Interactive tests are indispensable when a software developer wants to answer these questions.
This article will introduce the JDemo framework and its techniques for writing code for interactive testing. It will also show the benefits that can be gained from writing demo code.
Let's have a look at a simple example component. Figure 1 shows the API of MyDice,
a Swing component for showing a die having a specified or random value.

Figure 1. MyDice example class for showing a die as a Swing component
Instead of only looking at the API, we are much more interested in what the die actually looks like
on the screen. A very popular approach for this is to add a simple main method to the
component class:
public static void main(String[] args) {
JComponent diceComponent = new MyDice();
JFrame frame = new JFrame("MyDice");
Container contentPane = frame.getContentPane();
contentPane.setLayout(new BorderLayout());
contentPane.add(diceComponent, BorderLayout.CENTER);
frame.setDefaultCloseOperation(
JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.show();
}
Running this class will open a frame showing a die on the screen. The die will have a random value,
since the default constructor from the MyDice class is being used.
A screenshot of the frame is shown in Figure 2.

Figure 2. Screenshot of a MyDice component
The main method solution in the previous section has some serious drawbacks. Having snippets of this
kind of demonstration code spread all over our production code gives a bad-code smell. The snippets contain
lots of duplication, sometimes add unnecessary dependencies, and are likely to break.
A better approach is to move the demo code to separate classes. Recently, Jonathan Simon described a technique called simulators. Simulators are special classes, written for graphically testing, or simulating, a component in various conditions. Usually, simulator classes are written from scratch and there is no assistance by frameworks or tools. Here and now we want to go a step further. First, let's write down a list of what we would like to have:
Removing anything non-essential from the demo code above leaves two simple lines of code, looking somewhat like this:
JComponent diceComponent = new MyDice();
show(diceComponent);
We want to create a dice component and then show it. Anything else does not matter much, and so we should not have to care about it. Using the JDemo framework, writing demos is just that easy.
Among others, JDemo provides an abstract base class SwingDemoCase, which contains many
convenience methods for Swing-based demos. One of these methods is for displaying a
JComponent object on the screen. So we can implement a demo class for the MyDice
component this way:
package de.jdemo.examples.dice.demo;
import de.jdemo.extensions.SwingDemoCase;
public class MyDiceDemo extends SwingDemoCase {
public void demo6Dice() {
JComponent diceComponent = new MyDice(6);
show(diceComponent);
}
public void demoRandomDice() {
JComponent diceComponent = new MyDice();
show(diceComponent);
}
}
There are two different ways to instantiate MyDice objects, so I have added two
demos to the MyDiceDemo class: 6Dice and RandomDice.
As can be seen in our little example, JDemo demo cases must be no-argument public
methods with a void return type. Their names start with
demo...() and they are implemented in subclasses of one of the abstract demo case
implementations, which are provided by the framework. Since we want to demonstrate a Swing-based
component, I have chosen SwingDemoCase as the superclass for the demo implementation.
Later in this article, we will have a look at the other JDemo base classes that are available.
The JDemo framework provides a Swing-based DemoRunner application for loading and executing demos. It can be started from the command line by specifying the name of the demo class as argument:
java de.jdemo.swingui.DemoRunner de.jdemo.examples.dice.demo.MyDiceDemo
Of course, we have to make sure that jdemo.jar and the MyDice example
(contained in the examples folder of the JDemo distribution) are in the CLASSPATH.
Figure 3 shows the DemoRunner when launched with our MyDiceDemo class.

Figure 3. JDemo DemoRunner running the MyDice demo
All available demos are listed as tree view in the upper half of the application. They can be
run by double-clicking on the tree items or by choosing one of the various actions
available in the context menu of each entry. The execution list in the lower half
shows the state of executed demos. In Figure 3,
you can see that the Random Dice demo is running. Figure 4 shows the frame JDemo has
opened on the screen in order to show the component.

Figure 4. Output of the Random Dice demo executed in JDemo
Having a look back to the wish list from the previous section, we can see that our demo implementation meets all of the requirements.
You already might have realized that JDemo DemoCases are similar to JUnit TestCase implementations. The reason for this is that the framework is based on the concepts, simplicity, and source code of JUnit. Developers familiar with unit testing will only have to learn a few things in order to use JDemo. Here is a list of the main aspects and differences of which one has to be aware:
demo...() rather than with test...().assert...() statements. Each demo method must instead contain a call to a show...() method.setUp() and tearDown() methods, which (just as in JUnit) will be executed before or after the demo method, respectively.It has often has been claimed that unit tests are good example code. But in my opinion, in most cases this just isn't true. In unit tests, I almost never saw suitable examples for how to use the provided API. This is because unit tests usually do not implement practical examples. Demos, on the other hand, are practical examples, so it is much more likely that good example code will be found there. In fact, practice has shown that combining test-driven development with writing demos is a great combination, with JUnit and JDemo complementing each other.
|
Just like JUnit test cases, JDemo demo cases can be arranged in suites. Demo suites use the composite pattern in order to create a new demo containing multiple demo classes. A demo suite can then be passed to the DemoRunner in order to have more than one demo class available for execution.
Here is a simple example:
package de.jdemo.examples.dice.demo;
import de.jdemo.framework.DemoSuite;
import de.jdemo.framework.IDemo;
public class AllDemos {
public static IDemo suite() {
DemoSuite suite = new DemoSuite(
"All Demos for MyDice");
suite.addDemo(new DemoSuite(MyDiceDemo.class));
return suite;
}
}
This example creates a demo suite only containing our MyDiceDemo demo class.
Of course, demo suites themselves can then be added to other suites, and so on. A well-maintained
set of demos will then form a tree hierarchy. Figure 5 shows a demo tree for a more
complex example application.

Figure 5. A well-structured tree of demos for a more realistic software example
Let's have another look at the demos in Figure 5. Such a complete set of demos can also be very valuable when you have to work on existing code libraries. You do not need to start the complete application, log in to a database, and load example data, all in order to have a look at the export dialog. Instead you just start the demos and select the relevant component from the DemoRunner.
You also will not have to crawl the source code to find the relevant code; with JDemo,
you can link the demo source code to the DemoRunner application. Let's try this for our MyDice example
by specifying the path to the source code as sourcepath when starting the
DemoRunner application:
java de.jdemo.swingui.DemoRunner -sourcepath . de.jdemo.examples.dice.demo.MyDiceDemo
Similar to the CLASSPATH, the sourcepath is a list of directories
or .zip or .jar files. Here we have specified the current directory to contain the source code.
Now we have a context menu item, Show source code, at each demo in the DemoRunner (see Figure 6).

Figure 6. A menu item for showing the source code from within the DemoRunner
By default, the JDemo DemoRunner shows the source-code syntax highlighted (using the Java2Html library) in a separate window (see Figure 7). Note that when you are using the JDemo plugin available for the Eclipse IDE, the source code will be shown directly in the development environment.

Figure 7. Syntax highlighted source code as shown by the DemoRunner
The JDemo framework provides many options and advanced techniques that cannot all be covered in this article. However I want to give a quick introduction to one of the most interesting techniques: capturing demo output automatically.
As each demo shows its object for demonstration using a show...() method from the
framework, it is not very hard to capture this output automatically. For GUI-based demos,
the captured output is a screenshot taken by using the java.awt.Robot class.
The following Ant build script shows how we can take
a screenshot from our MyDice demo:
<project default="screenshot">
<taskdef name="demoGuiCapture"
classname=
"de.jdemo.capture.anttasks.GuiDemoCaptureTask"
classpath="jdemo.jar"
/>
<target name="screenshot">
<demoGuiCapture
demoId=
"de.jdemo.examples.dice.demo.MyDiceDemo:demo6Dice"
outputFile="dice_screenshot.png"
includeTitle="false"
/>
</target>
</project>
First a new Ant task, demoGuiCapture, is defined. Again, we have to make sure
that jdemo.jar is on the specified CLASSPATH.
The screenshot target uses the demoGuiCapture task for taking a screenshot
from the 6Dice demo. Also note that by setting the includeTitle option to false, we only capture the MyDice component, not the complete frame.
Figure 8 shows the captured image.

Figure 8. Screenshot automatically taken of the 6Dice demo
As a matter of fact, all of the screenshots in this article were taken automagically by using JDemo (even Figure 6, which actually is a composition of two demos).
JDemo is not limited to Swing-based demos. The framework comes with four base classes for different kinds of demos:
| JDemo base class | Description |
|---|---|
de.jdemo.framework.DemoCase |
Demos showing text (java.lang.CharSequence) or File objects. |
de.jdemo.extensions.AwtDemoCase |
Demos based on components from the Java AWT (Abstract Windowing Toolkit). |
de.jdemo.extensions.SwingDemoCase |
Swing-based demos--as described in this article. Since Swing is based on AWT, SwingDemoCase subclasses AwtDemoCase. |
de.jdemo.extensions.SwtDemoCase |
Demos based on the Eclipse SWT (Standard Widget Toolkit). |
In practice, when writing demos, you will probably subclass one of the provided classes directly. Usage examples can be found in the examples folder of the JDemo download package. Their API is described in detail in the JavaDoc API documentation and the JDemo online documentation.
In this article we have learned how the JDemo framework can be used to organize demonstration and example code by writing demo case classes. Like JUnit test cases, JDemo demo cases can be organized in suites and run from the provided DemoRunner application.
We also have seen that demos are not only useful during the development of a software component, but can also be very valuable later. Demos can be used to directly access all of the software components in a large library. They also provide example code for how to use the API properly. As an additional benefit, GUI demos can be used for automatically taking screenshots; e.g., for documentation purposes.
This article could only show a few aspects of the framework. In order to learn more about it and its capabilities, I recommend reading the detailed online documentation or just checking JDemo out on your next software project.
Markus Gebhard works as software engineer at disy Informationssysteme GmbH, in Karlsruhe, Germany.
Return to ONJava.com.
Copyright © 2007 O'Reilly Media, Inc.