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;
public class EmptyTest extends JellyTestCase {
public EmptyTest(String testName) {
super(testName);
}
public static void main(java.lang.String[] args) {
junit.textui.TestRunner.run(suite());
}
public static NbTestSuite suite() {
NbTestSuite suite = new NbTestSuite();
suite.addTest(new EmptyTest("test1"));
suite.addTest(new EmptyTest("test2"));
return suite;
}
public void test1() {
}
public void test2() {
}
}
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):
CopyAction copyAction = new CopyAction();
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):
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();
Node node = new Node(pto.tree(), "JellytoolsExamples|Source Packages|examples");
Node node1 = new Node(node, "NodesTest.java");
node1.select();
JavaNode javaNode = new JavaNode(node, "NodesTest.java");
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):
PropertySheetOperator pso = new PropertySheetOperator("PropertiesTest.java");
Property p = new Property(pso, "Encoding");
System.out.println("PROPERTY: "+p.getName()+"="+p.getValue());
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):
p.openEditor();
StringCustomEditorOperator customEditor = new StringCustomEditorOperator("Encoding");
System.out.println("Encoding value="+customEditor.getStringValue());
customEditor.setStringValue("ASCII");
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):
NewFileWizardOperator nfwo = NewFileWizardOperator.invoke();
nfwo.selectProject("JellytoolsExamples");
nfwo.selectCategory("Java Classes");
nfwo.selectFileType("Java Class");
nfwo.next();
NewFileNameLocationStepOperator nfnlso = new NewFileNameLocationStepOperator();
nfnlso.txtObjectName().typeText("MyNewClass");
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:
String confirmTitle = Bundle.getString("org.openide.explorer.Bundle", "MSG_ConfirmDeleteObjectTitle");
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.