FeaturesPluginsDocs & SupportCommunityPartners
Author: Alexandre Iline , Jiri Skrivanek
Last modified: April 13, 2005

Writing Jelly Tests in NetBeans

Related modules: Jemmy | Test Tools | Jemmy Support | XTest

Table of Contents:


Introduction

This document describes how to write GUI tests for NetBeans IDE using the Jelly library. Jelly contains classes to cover most of the functionality necessary to work with NetBeans IDE core GUI components and windows.

See Writing Operators Guide for instructions how to create classes covering non-core (i.e. module specific) NetBeans GUI parts.

Jelly is based on the Jemmy library. Jelly provides direct access to Jemmy functionality, so you can use Jemmy in your tests directly. (See more info about using Jemmy below). However, it would be highly recommended to put as much functionality as possible into module specific operators.

To demonstrate Jellytools operators we prepared several examples (examplesProject.zip) which will be used in this document.


How to execute tests

UI tests based on Jellytools can work only if they are executed in the same JVM as the tested application. In order to fulfill this requirement you need to use a special action provided by the JemmySupport module. Steps to run Jellytools examples are as follows:
  • run NetBeans IDE
  • install modules Jellytools, Jemmy Module, JemmySupport and NB JUnit (you can either build these modules yourself or download them from the NetBeans Update Center - main menu Tools|Update Center - under category Testing Tools)
  • unzip examplesProject.zip to a working directory
  • open the JellytoolsExamples project in the NetBeans IDE
  • check that under the Libraries node are Jellytools, Jemmy, JUnit and NB JUnit libraries
  • open or select a test you want to execute
  • run the test by using the 'Run Internally' action from the JemmySupport toolbar (or use the shortcut Ctrl+Alt+F6 or main menu item Tools|Run Internally)
  • output is printed to the console (on Windows start NetBeans with the nb.exe launcher to open the console)


How to create tests

Tests are based on the JUnit framework.  We recommend using JellyTestCase as a superclass. It performs correct Jemmy initialization and handles test case exceptions. Tests which should run daily can be deployed in a test harness. In NetBeans we use the XTest harness.

Example:

import org.netbeans.jellytools.JellyTestCase;
import org.netbeans.junit.NbTestSuite;

/** This is just an example of how the test could look. */
public class EmptyTest extends JellyTestCase {

/** Constructor required by JUnit */
public EmptyTest(String testName) {
super(testName);
}

/* Method allowing test execution directly from the IDE. */
public static void main(java.lang.String[] args) {
junit.textui.TestRunner.run(suite());
}

/** Creates suite from particular test cases. */
public static NbTestSuite suite() {
NbTestSuite suite = new NbTestSuite();
suite.addTest(new EmptyTest("test1"));
suite.addTest(new EmptyTest("test2"));
return suite;
}

/** Test case 1. */
public void test1() {
//test case 1
}

/** Test case 2. */
public void test2() {
//test case 2
}
}
This test does not really do anything - working code should be in the test1(), test2() methods. Pay attention, though, to the main(String[]) method. It's optional, however, it's a good idea to have this method - it allows running the test directly from the IDE (see above).

To get various ideas you can also take a look at Jellytools internal tests.
 

JellyTestCase

JellyTestCase is an extension of NbTestCase intended for Netbeans GUI testing explicitly.

JellyTestCase initializes Jemmy. Contents of that initialization can be changed at any time. For now, it initializes output so test output is really going to a log provided by NbTestCase and sets some timeouts necessary for NetBeans GUI testing.

Any JemmyException (which is normally thrown as a result of an unsuccessful operation in Jemmy) going from a test is treated by JellyTestCase as a test failure; any other exception - as a test error.

Using its public switches you can define whether a test should

  • close all modal dialogs at the end of the test case (switch jemmy.close.modal - default true)
  • generate component dump (XML file containing components information) in case of test failure (switch jemmy.screen.xmldump - default false)
  • capture screen into a PNG file in case of test failure (switch jemmy.screen.capture - default true)
  • wait at least 1000 ms between test cases (switch jelly.wait.no.event - default true)



What to use

Jelly classes could be divided into five structural parts:

Operators

Jemmy operators is a set of classes which are test-side agents for application components. Operators provide all possible methods simulating user action with components, methods to find and wait components and windows. Also operators map all components methods through the event queue used for event dispatching. All Jellytools operators are subclasses of Jemmy operators.

All of the operators provide access to their subcomponents by "getters" methods. These methods are implemented using the "lazy initialization" technique, so real suboperator instances are not initialized until it's necessary. All of the suboperators are initialized by verify() method invocation, so this method guarantees that all subcomponents are already loaded.

Getters method names usually have a prefix showing component type, the rest of the names usually repeat component text or text of the corresponded label: JButtonOperator btCancel(), JRadioButtonOperator rbSubString(), JTextFieldOperator txtName(). You can see a full list of prefixes in a table shown in the Writing Operators Guide.

Some of the operators have an invoke() method which causes component displaying using an action. You can use these actions directly - their names usually look like *ViewAction (RuntimeViewAction for RuntimeTabOperator).

Most common Jellytools operators:
  • TopComponentOperator - is an operator for org.openide.windows.TopComponent component. It represents all dockable views in the IDE like Projects view, Editor views and others. TopComponentOperator can be used where no special operator like ProjectsTabOperator is not implemented.
  • NbDialogOperator - is the common superclass for all dialog operators in Jelly. It has some common methods for pushing the most used buttons on dialogs: "OK", "Cancel", "Close", "Help", "Yes", "No".
  • TreeTableOperator - handles the IDE's org.openide.explorer.view.TreeTable component which is used instead of JTree, for example in the Options dialog and SetupWizard
  • MainWindowOperator - handles the NetBeans main window. It manipulates with toolbars and you can get text from the status bar.
  • ProjectsTabOperator, FilesTabOperator, FavoritesOperator, RuntimeTabOperator - specialized TopComponentOperators for tabs in the IDE
  • EditorOperator - handles an editor top component in the NetBeans IDE. It enables you to get, select, insert or delete text, move caret, work with annotations and with toolbar buttons.
  • OutputTabOperator - operator to get text from the output tab.
Look at OperatorsTest.java file.

Actions

Action classes mission is to replace menu and popup calls.

Most of the actions inside the NetBeans IDE could be done in some different modes. org.netbeans.jellytools.actions.Action class is designed to describe all the modes in which the action could be performed:

  • by menu
  • by popup
  • by shortcut
  • by direct API call
Not all of the actions can be performed by all modes above, however, most of the menu/popup operations in Netbeans can also be done by shortcuts and direct API calls. Keeping that in mind, try to use a properly created Action instance whenever you want to perform menu or popup operations. Even if you do care to perform the operation exactly by menu (popup), create an action. The very same code could be used in a different place in another mode.

Using the perform() method of any action will perform the action by the first available mode that could be performed. The sequence of modes to be tried is defined by the Action.setDefaultMode(int) value.

Example (from ActionsTest.java):

        // create copy action
CopyAction copyAction = new CopyAction();
// call Copy popup menu item on the node (default mode)
copyAction.perform(node);
Example above is performed in default mode. Copy action can also be performed in any of the other available modes.

Example (from ActionsTest.java):

        // perform action in different modes on a node
copyAction.performMenu(node);
copyAction.performShortcut(node);
copyAction.performAPI(node);
copyAction.performPopup(node);
Actions can also be performed on a node, array of nodes, and ComponentOperator. If the action is performed on a node (nodes), node is selected first.

Action classes are in the org.netbeans.jellytools.actions package and module specific in org.netbeans.jellytools.modules.<modulename>.actions packages.

Nodes

Node objects are designed to make tree operations easier. Node encapsulates information about the tree it's displayed in and about the tree path. Node classes can be used to perform applicable operations to the nodes in any tree in the IDE. Nodes can also be passed into Action.perform*(Node) methods.

There are some useful node types defined in Jellytools, such as: ClassNode, FolderNode, ProjectRootNode.

Example (from NodesTest.java):

        ProjectsTabOperator pto = new ProjectsTabOperator();
// find node in a given tree
Node node = new Node(pto.tree(), "JellytoolsExamples|Source Packages|examples");
// find node under given parent node
Node node1 = new Node(node, "NodesTest.java");
// select node
node1.select();
// create instance of specialized JavaNode
JavaNode javaNode = new JavaNode(node, "NodesTest.java");
// call predefined action
javaNode.copy();
Nodes classes are in the org.netbeans.jellytools.nodes package and org.netbeans.jellytools.modules.<modulename>.nodes packages.

Properties

org.netbeans.jellytools.properties package contains classes to work with properties. Besides PropertySheetOperator, it contains classes - inheritors of org.netbeans.jellytools.properties.Property class.

Property value can be changed by inline editors (text field for string values, check box for boolean values - setValue(String) method and combo box for list values - setValue(int) method.

Example (from PropertiesTest.java):

        // find property sheet with given title
PropertySheetOperator pso = new PropertySheetOperator("PropertiesTest.java");
// find property Encoding
Property p = new Property(pso, "Encoding");
// get name and value
System.out.println("PROPERTY: "+p.getName()+"="+p.getValue());
// set new value
p.setValue("ASCII");

Any property may or may not have a "..." button invoking a property editor. If a property has this button, it can have any number of set*Value(*) methods using the editor for property editing.

Some properties might be not editable directly (other than by the property editor). They extend Property and override setValue(*) methods to use the property editor. But if a property is editable directly, which most of them are, setValue(*) methods work directly.

Property editors are another class of operators which are located inside the org.netbeans.jellytools.properties.editors package and org.netbeans.jellytools.modules.<modulename>properties.editors packages.

Examples of property editors are: ColorCustomEditorOperator, DimensionCustomEditorOperator, FileCustomEditorOperator, PointCustomEditorOperator, StringCustomEditorOperator.

Property editor can be invoked by the Property.openEditor() method.

Example (from PropertiesTest.java):

        // open custom editor for property (...)
p.openEditor();
// find custom editor
StringCustomEditorOperator customEditor = new StringCustomEditorOperator("Encoding");
// get value from custom editor
System.out.println("Encoding value="+customEditor.getStringValue());
// set value
customEditor.setStringValue("ASCII");
// confirm custom editor dialog
customEditor.ok();

Wizards

There are some operators in Jelly providing functionality covering NetBeans wizards: WizardOperator, NewFileWizardOperator, NewFileNameLocationStepOperator, NewProjectWizardOperator, NewProjectNameLocationStepOperator, NewWebProjectNameLocationStepOperator, NewWebProjectSourcesStepOperator.

Example (from WizardsTest.java):

        // open new file wizard
NewFileWizardOperator nfwo = NewFileWizardOperator.invoke();
nfwo.selectProject("JellytoolsExamples");
nfwo.selectCategory("Java Classes");
nfwo.selectFileType("Java Class");
// go to next page
nfwo.next();
// create operator for the next page
NewFileNameLocationStepOperator nfnlso = new NewFileNameLocationStepOperator();
nfnlso.txtObjectName().typeText("MyNewClass");
// finish wizard
nfnlso.finish();


Using bundles.

Moving most of the functionality into operators makes tests live longer with no changes in the tests themselves, because most of the changes in tested code can be covered by operators changing. However, some of the functionality still could be uncovered.

One of the places where tests are tied to tested code version is string resources. Again, most of the resources will be used in operators. But the rest of them should be taken from NetBeans bundles, so even if a resource is changed for NetBeans, the tests use the right resource value anyway.

It can be done using the org.netbeans.jellytools.Bundle class. To know which bundle and key is used for a particular label, look at the sources or use other available methods.

Example:

        // "Confirm Object Deletion" dialog title
String confirmTitle = Bundle.getString("org.openide.explorer.Bundle", "MSG_ConfirmDeleteObjectTitle");
// "Help" main menu item
String helpItem = Bundle.getStringTrimmed("org.netbeans.core.Bundle", "Menu/Help");
Using a Bundle class automatically makes I18N application testing possible, because localized resource values will be used instead.

Using Jemmy in tests

Classes provided by Jelly have most methods necessary to perform operations in the "usual" way. So, if you do not need to do something "unusual", methods you need are probably there already.

As was mentioned above Jelly operators extend Jemmy operators, so you can use them as regular Jemmy operators. Additionally, most of the Jelly operators have shortcuts to get operators for most of their subcomponents. These operators are, again, either Jemmy operators or Jelly operators. So there is no need to create any operator yourself.




Companion
Projects:
MySQL Database Server   Open JDK: an Open SourceJDK   GlassFish Community: an Open Source Application Server    Mobile & Embedded Community    Open Solaris   java.net - The Source for Java Technology Collaboration   Open ESB - The Open Enterprise Service Bus Powered by