Jellytools Frequently Asked Questions
Last update: July 3-rd, 2006
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:
new CopyAction().perform();
new CopyAction().performMenu();
It also possible to specify a node or component on which an action
will be performed:
new FindAction().perform(myNode);
new FindAction().performPopup(myComponentOperator);
If an action is not predefined, you can create and use your own
action.
new Action("Edit|Copy", null).perform();
new Action(null, "Copy").perform(myNode);
new ActionNoBlock("Edit|Find", null).perform(myFolderNode);
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:
SourcePackagesNode sourceNode = new SourcePackagesNode("My Project");
Node node = new Node(sourceNode, "org.netbeans.jellytools.nodes|Node.java");
System.out.println(node.getText());
new OpenAction().performAPI(node);
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();
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 distinguished 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 Node located in the example above:
Node methodsNode = new Node(node, "Node|Methods");
Node methodNode1 = new Node(methodsNode, 5);
methodNode1.select();
Node methodNode2 = new Node(methodsNode, 6);
methodNode2.select();
Q: How to wait for a new nodes
when
JTree is re-generated after it is added? (tree re-generation)
A: If you use org.netbeans.jellytools.Node
class, it is guaranteed that all nodes are ready before an attempt to
access them. But it still 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 cast 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:
Operator.DefaultStringComparator comparator = new Operator.DefaultStringComparator(true, true);
Operator.StringComparator oldComparator = Operator.getDefaultStringComparator();
Operator.setDefaultStringComparator(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.
anOperator.setComparator(comparator);
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("NetBeans.*");
new JFrameOperator("Net.eans.*");
Q: My test doesn't find any
component
in IDE.
A: Check if you run your test case internally in IDE. 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
closed.
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 of 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 JemmyException (extends
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 "Copy" under
Edit
menu in NetBeans IDE, we need to call Bundle.getStringTrimmed("org.openide.actions.Bundle", "Copy"). 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
parameters
-nosplash -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.
You can also use 'Resource bundle lookup' feature from jemmysupport
module.
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.
Q: How to
fix "Couldn't execl robot child process: Permission denied"?
A: On Unix it can happen that
test fails because of "Couldn't execl robot child process: Permission
denied" error when initialization of Robot. To resolve this issue you
have to repair file permissions as stated in this contribution
http://zzlinux.blogspot.com/2004/12/couldnt-execl-robot-child-process.html:
"execl" is a system call, So, the api
was trying to run a native application to assume the controls of the
keyboard and mouse. When jvm tries to create a child process to execute
this function, it receive the message from OS (linux): "Permission
Denied". Search for two files on directory jre/lib/i386: awt_robot and
awt_robot_g. Then use "chmod +x" on both files as root and the Robots
will be created without error messages.