Jellytools Frequently Asked Questions - NetBeans 3.6
Last update: January 27, 2003
Contents:
Q: How to invoke a popup or main menu item? (Actions)
A: The recommended way how to invoke popup or main menu is through
an Action or ActionNoBlock instance. Jellytools include
bunch of predefined actions. They can be found in package org.netbeans.jellytools.actions
and they are descendants either of Action for non blocking operations
or of ActionNoBlock for operations which may block further execution
by a modal dialog showing.
An action can be performed in "main menu", "popup", "shortcut" or "API" modes.
By default it is performed in the first available mode because not every
action is defined for all four modes. An example of usage can be:
// performs in default mode
new CopyAction().perform();
// performs in "menu" mode
new CopyAction().performMenu();
It also possible to specify a node or component on which an action will
be performed:
// selects node first and then performs on it
new FindAction().perform(myNode);
// focuses component first and then performs action in "popup"
mode
new FindAction().performPopup(myComponentOperator);
If an action is not predefined, you can create and use your own action.
// invokes main menu item "Edit|Copy"
new Action("Edit|Copy", null).perform();
// invokes popup menu item "Copy" on given node
new Action(null, "Copy").perform(myNode);
// invokes main menu item "Edit|Find" (first selects given
node)
new ActionNoBlock("Edit|Find", null).perform(myFolderNode);
// invoke popup menu item "Find" on given component
new ActionNoBlock(null, "Find").perform(myEditorOperator);
Additional details about actions can be found at the Writing
Jelly Test Guide.
Q: What are nodes good for?
A: Nodes should help to easier testing of JTree's. The most frequent
usage in IDE is in the Explorer Window but nodes can be used in any component
which includes a JTree instance. Nodes are also used as parameters for action's
performing.
In package org.netbeans.jellytools.nodes there can be found predefined
specialized nodes for common objects in IDE. You can use them with combination
of generic node instances as you like. Simple example looks like this:
// gets tree from Filesystems tab of the Explorer Window
JTreeOperator treeOperator = ExplorerOperator.invoke().repositoryTab().tree();
// finds folder "jellytools" under filesystem "jellytools/src"
FolderNode folderNode = new FolderNode(treeOperator, "jellytools/src|org|netbeans|jellytools");
// selects the node
folderNode.select();
// finds child node under jellytools folder
Node node = new Node(folderNode, "nodes|FolderNode");
// selects the node
node.select();
Items in a node path are separated by "|" character and searching (exact
match, case sensitivity) is driven by current string comparator. Root node
must not be included in search path. To get root node instance you need to
supply empty search path or use a getter method:
Node rootNode = new Node(treeOperator, "").select();
// or
Node rootNode = new ExplorerOperator().repositoryTab().getRootNode();
Additional details about nodes can be found at the Writing
Jelly Test Guide.
Q: How to distinguish nodes with the same
display name?
A: Nodes with the same display name can be distinguish by their
index under parent node. If the index is not permanent, it can be computed
with help of parentNode.getChildren() method. Following example enables
to access method nodes under class FolderNode located in above example:
// finds parent node
Node methodsNode = new Node(node, "Class FolderNode|Methods");
// locate twelfth node under parent node
Node newFromTemplateNode1 = new Node(methodsNode, 11);
newFromTemplateNode1.select();
// locate thirteenth node under parent node
Node newFromTemplateNode2 = new Node(methodsNode, 12);
newFromTemplateNode2.select();
Q: How to wait for a new nodes when
JTree is re-generated after it is added? (tree re-generation)
A: It might happen that you need to find a node but its tree is
re-created after this node is added. In that case this code doesn't work:
Node subChildNode = new Node(tree, "parent|child|subChild");
First you have to wait for presence of required node and then you can
locate it in the tree hierarchy:
try {
new Waiter(new Waitable() {
public Object actionProduced(Object
parent) {
return new
Node((Node)parent, "child").isChildPresent("subChild") ? Boolean.TRUE: null;
}
public String getDescription() {
return("Sub
child present under child");
}
}).waitAction(parent);
} catch (InterruptedException e) {
throw new JemmyException("Interrupted.", e);
}
Node subChildNode = new Node(tree, "parent|child|subChild");
Q: How to separate menu items or tree nodes
in paths parameters?
A: The default separator of menu items and tree node is "|" character.
Q: How to get instance of component from an
operator?
A: In case you need to obtain instance of component from an operator,
use method getSource() and retype to an appropriate class:
JTree jTree = (JTree)treeOperator.getSource();
Q: How to change matching criteria? (String
comparator)
A: By default all string comparison is done case insensitively and
not exactly (substring match). To change criteria you need to set a new comparator.
Comparator is an implementation of interface Operator.StringComparator.
Operator.DefaultStringComparator is basic implementation which enables
to set if substring match is used and case sensitivity. To set comparator
for all forthcoming operations use static method setDefaultStringComparator().
Be careful. It has to be called before any operator is created. In other words,
only newly created operators will use new comparator:
// create exactly (full match) and case sensitively comparing
comparator
Operator.DefaultStringComparator comparator = new Operator.DefaultStringComparator(true,
true);
// store previously used comparator
Operator.StringComparator oldComparator = Operator.getDefaultStringComparator();
// set new comparator
Operator.setDefaultStringComparator(comparator);
// here every newly created operator will use new comparator
//....
// restore previous comparator
Operator.setDefaultStringComparator(oldComparator);
To set comparator only for a particular operator instance, you need to
use method setComparator(). Once you have set comparator for
operator instance, comparator is also propagated into newly created operators
where this operator is supplied as parameter in a constructor.
// set comparator for this instance
anOperator.setComparator(comparator);
// also this operator instance will have the same comparator
set
JButtonOperator jButtonOperator = new JButtonOperator(anOperator);
In Jemmy documentation you can
learn more about operators environment.
Q: Is it possible to use regular expressions?
A: Yes, it is! You only need to set org.netbeans.jemmy.util.RegExComparator
as default and then you are able to use all valid regular expression patterns
(see java.util.regex.Pattern javadoc for instance):
RegExComparator regExComparator = new RegExComparator();
Operator.setDefaultStringComparator(regExComparator);
new JFrameOperator("Explorer.*");
new JFrameOperator("E.plorer.*");
Q: My test doesn't find any component
in IDE.
A: Check if you run your test case by Internal Execution. Go to
the Explorer window and show properties window of your test class. On the
Execution tab select "Internal Execution" from Executor property's combo.
UI test based on jelly can work only if it is executed in the same JVM as
tested application because it allows to use all Java API for test purposes.
Q: Why use no block methods?
A: There exist so called "no block" methods. They have to be used
when a modal dialog is opened as the result of an action. If we don't use
"no block" method, test execution will be blocked until modal dialog is opened.
Q: Is it possible to redirect or turn output
messages off?
A: Majority of messages is produced by Jemmy on which jelly
is built. Jemmy uses three different outputs. To the trace output is sent
as much information as it is known. To the error output only error messages.
The golden output contains only time and environment independent messages
which can be used for golden files test techniques. There exist JemmyProperties.setCurrentOutput()
methods that enable to redirect these messages to specified PrintWriters.
For example, JemmyProperties.setCurrentOutput(new TestOut(System.in,
myOutPrintWriter, myErrPrintWriter, myGoldenPrintWriter));. As default
all output is sent to standard output.
In order to suppress some kind of output messages you can use null
as the destination of messages. To disable all output and error messages you
need to call JemmyProperties.setCurrentOutput(TestOut.getNullOutput());.
Q: What is relation between jemmy and jelly?
A: Jemmy is a library which allows you to create automated tests
of Java GUI application. It is NOT dependent on NetBeans IDE. Jelly is based
on Jemmy and it is a library of components helping to write GUI tests of
NetBeans IDE.
Q: How to detect a test failure?
A: A test case should consist of pairs action and verification.
It is possible to omit verification, if the next action depends on result
of previous action. Actually, verification in that case is hidden because
we are waiting for some object to make next action on it. If specified time
expires, waiting fails and some RuntimeException is thrown. This exception
can be caught in our test case or it is propagated to a test harness in which
the test case runs.
We can also throw exception ourselves, if we detect some unexpected result
of an action. Or instead of exception we can use harness specific methods
to signal a failure.
Golden files technique is also a possibility. You collect output to a specified
file and at the end you compare its content to reference file. If files differ,
test fails. This approach is supported usually by harness.
Q: How to localize test cases?
A: To identify components, jemmy and jelly use mainly window's titles,
button's label and other strings bound with searched component. It is good
practice to collect such strings in Bundle.properties files to enable their
later localization. We can benefit from such approach and use strings directly
from bundles. If there are not hard coded strings in our test cases, they
will work on every locale to which tested product is translated. For example
to get localized string for menu item "Search Filesystems..." under Edit
menu in NetBeans IDE, we need to call org.netbeans.jellytools.Bundle.getString("org.netbeans.modules.search.Bundle",
"TEXT_ACTION_REPOSITORY_SEARCH"). We have to know location of Bundle.properties
file in java hierarchy and key of searched string. NetBeans IDE contains support
for easier investigation of string source. If you run IDE with parameter
-J-Dorg.openide.util.NbBundle.DEBUG=true, every string in
IDE is followed by ordinal number of bundle file and line number of the key
within that file. By the ordinal number you can seek bundle file origin in
console output. Once you know path to bundle, you need to find this file
within IDE sources or in IDE's jar files, open it and look at given line.
Q: What is the robot mode?
A: Robot mode is the mode of jelly in which GUI actions are done
through java.awt.Robot. The Robot class generates native system input events.
It is closer to user input but it is more fragile for debugging end execution.
Q: How to debug test cases in IDE?
A: See step by step guide.