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 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 Jemmy library. Jelly provides direct access to Jemmy functionality, so you can use Jemmy in your tests directly. (See more info about Jemmy using below). However, it'd 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 test

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


How to create test

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

Example:

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

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

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

/* Method allowing to execute test directly from 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 test1(), test2() methods. Pay attention, though, to the main(String[]) method. It's optional, however, it's good idea to have this method - it allows to run the test directly from 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 any time. As for now, it initializes output so test output is really going to log provided by NbTestCase and set some timeouts necessary for NetBeans GUI testing.

Any JemmyException (which is normally thrown as a result of 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 test should

  • close all modal dialog at the end of 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 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 four 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 in "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 using guarantee that all subcomponents are already loaded.

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

Some of the operators have invoke() method which causes component displaying using an action. You can use these actions directly - their names are 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 IDE like Projects view, Editor views and other. TopComponentOperator can be used where not special operator like ProjectsTabOperator is not implemented.
  • NbDialogOperator - is common superclass for all dialog operators in Jelly. It has some common methods pushing most used buttons on dialog: "OK", "Cancel", "Close", "Help", "Yes", "No".
  • TreeTableOperator - handles IDE's org.openide.explorer.view.TreeTable component which is used instead of JTree for example in Options dialog and SetupWizard
  • MainWindowOperator - handles NetBeans main window. It manipulates with toolbars and you can get text from status bar.
  • ProjectsTabOperator, FilesTabOperator, FavoritesOperator, RuntimeTabOperator - specialized TopComponentOperators for tabs in IDE
  • EditorOperator - handles an editor top component in NetBeans IDE. It enables to get, select, insert or delete text, move caret, work with annotations and with toolbar buttons.
  • OutputTabOperator - operator to get text from output tab.
Look at OperatorsTest.java file.

Actions

Action classes mission is to replace menu and popup calls.

Most of the actions inside NetBeans IDE could be done in some different modes. org.netbeans.jellytools.actions.Action class is designed to describe all the modes 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 properly created Action instance whenever you want to perform menu or popup operations. Even if you do care to perform operation exactly by menu (popup), create an action. The very same code could be used in different place in another mode.

Using perform() method of any action you will perform action by the first available mode it could be performed. Sequence of modes to be tried is defined by 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 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 node, array of nodes, and ComponentOperator. If action is performed on a node (nodes), node is selected first.

Action classes are in 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 tree it's displayed in and about tree path. Node classes can be used to perform applicable operations to the nodes in any tree in 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 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 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 (fro 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 "..." button invoking a property editor. If property has this button, it can have any number of set*Value(*) methods using the editor for property editing.

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

Property editors are another class of operators which are located inside 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 Property.openEditor() method.

Example (fro 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 functionality into operators makes tests live longer with no changes in tests itself, because most of the changes in tested code can be covered by operators changing. However, some of 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 gotten from NetBeans bundles, so even if a resource is changed for NetBeans, tests uses right resource value anyway.

It can be done by org.netbeans.jellytools.Bundle class using. To know which bundle and key is used for particular label look at 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 Bundle class automatically makes possible of I18N application testing, because localized resource values will be used instead.

Using Jemmy in tests

Classes provided by Jelly have most methods necessary to perform operations in "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   Virtual Box - full virtualizer  Open ESB - The Open Enterprise Service Bus Powered by