ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

JDemo: Interactive Testing Refactored JDemo: Interactive Testing Refactored

by Markus Gebhard
09/08/2004

Today'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:

  • Is the layout correct?
  • Do the components react as expected?
  • What happens when I resize the dialog window?

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.

Example

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.

API for MyDice class
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.

Screenshot of MyDice component showing a dice having a random value
Figure 2. Screenshot of a MyDice component

Demo Code Refactored

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:

  • Demo code should be separated from production code.
  • We want to be able to write multiple coexisting demos for a single component.
  • Writing and running demos should be an organized process.
  • Writing demos should be as easy as writing unit tests.
  • Demos should just contain essential code--we do not want to care about creating frames, initializing event handling, etc.

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.

Writing Our First JDemo Demo Cases

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.

JDemo DemoRunner launched on MyDice demo
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.

Random Dice demo executed in DemoRunner
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.

JUnit as a Role Model

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:

  • Test cases and test suites in JUnit are analogous to demo cases and demo suites in JDemo.
  • Demo method names start with demo...() rather than with test...().
  • In demos, there are no assert...() statements. Each demo method must instead contain a call to a show...() method.
  • Demos are not run automatically, but rather presented to the user in a hierarchical view. The user can pick and start the demos arbitrarily.
  • Demos are not meant to ensure that the implementation works as specified, but rather to demonstrate the code usage and the output to the user.
  • Demo cases can implement 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.

Pages: 1, 2

Next Pagearrow