Jemmy Module - Tutorial
This tutorial below doesn't expose Jemmy functionality very much.
It mostly shows how to use the most hi-level jemmy part - operators.
It also provides some references to the lower-level functionality.
Table of Contents
Downloading
Test class creation
Application execution
Window searching
Component searching
Access to swing component
Actions with component
Nonblocking actions
Event queue using
Jemmy exceptions
String resources
Timeouts
Drivers
Robot vs event dispatching.
Timeouts and resources location.
Test execution.
Test stability.
See also:
all samples,
API and
Applet testing tips.
Downloading
First, you have to
download jemmy.jar
Test class creation
It must implement the
org.netbeans.jemmy.Scenario interface.
It must implement the
public int org.netbeans.jemmy.Test.runIt(Object)
method. This method, actually, is a test scenario.
Examples:
WaitWindowSample.java
or any other sample
Note:
Test class, generally, must not extend any special class
or implement any interface - it's up to you how to organize it. But here
we'll suppose that the test implements org.netbeans.jemmy.Scenario
interface.
Application execution
Jemmy test must be executed in the same JVM as the tested application
is executed in. The easiest way to do it is
org.netbeans.jemmy.ClassReference
class using.
Examples:
WaitWindowSample.java
or
any other sample
Note:
Application could be executed directly by public static main(String[])
invocation. ClassReference class using allows to avoid application
classes import.
Note:
Application may be able to run test from itself. In that
case you have to decide yourself way is better for you.
Window searching
To do anything with swing component, you have to find it first. To find
component you first have to find a window containing it.
Note:
Neither component nor window searching doesn't have a practical meaning
in most cases. So now, below when we really mean "wait" we say "find."
Low-level functionality is implemented in org.netbeans.jemmy.ComponentChooser
and org.netbeans.jemmy.WindowWaiter classes.
The most typical scenario is when a frame or dialog is searched by title.
Both
org.netbeans.jemmy.operators.JFrameOperator and
org.netbeans.jemmy.operators.JDialogOperator
have constructors with
java.lang.String parameter which allow
to find a frame or dialog. Dialog also can be found between its parent
children if we know the parent.
Examples:
WaitWindowSample.java,
WaitDialogSample.java
Component searching
As soon as we have found a window, we have a starting point to find
a component. Components can be searched inside the container.
org.netbeans.jemmy.operators.ComponentOperator and all its subclasses
(except operators for window-like components) have constructors with
org.netbeans.jemmy.operators.ContainerOperator parameter.
The rest of the parameters differ from component to component. Most operators
have constructors with
java.lang.String parameter which defines
a component string resource value.
Examples:
FindComponentsSample.java
Note:
Components also can be found by an ordinal index or by a string and an index.
The way to compare strings during searching:
org.netbeans.jemmy.operator.Operator.StringComparator
org.netbeans.jemmy.operator.Operator.setDefaultStringComparator(org.netbeans.jemmy.operator.Operator.StringComparator)
org.netbeans.jemmy.operator.Operator.setComparator(org.netbeans.jemmy.operator.Operator.StringComparator)
Access to swing component
Having an operator, you can get a component by
org.netbeans.jemmy.operators.ComponentOperator,getSource()
method. But, all operators map all components methods:
org.netbeans.jemmy.operators.AbstractButtonOperator.getText()
maps
javax.swing.AbstractButton.getText(). The difference between
((AbstractButton)abstractButtonOperator.getSource()).getText() and
abstractButtonOperator.getText() is that an operator invokes getText()
through the
event queue. So, normally it's better to use an operator's methods to access
components. It gives more test stability.
Note:
Operator's mapping method cannot be used if the code
itself is executed inside the queue. Using an operator's mapping method
will result in a deadlock in this case.
Note:
Behavior described above is used by default. You can,
though, change it. See Event queue using
for more info.
Actions with component
org.netbeans.jemmy.operators.ComponentOperator contains all methods
simulating user input by mouse and keyboard operations. Other operators
contain most methods necessary to drive components.
Examples:
ActionsSample.java,
TreeActionsSample.java,
TableActionsSample.java
Nonblocking actions
This section applies to non-robot mode only. Robot operations work well
when either any modal dialogs are displayed or not. Run the
ModalDialogSample.java
sample both in robot and nonrobot node to see this.
If a modal dialog is showed as a result of an action (such as button
pushing), the push method will never end and thus, test execution will
be blocked. In this case, one of the nonblocking methods have to be used:
org.netbeans.jemmy.operators.AbstractButtonOperator.pushNoBlock()
org.netbeans.jemmy.operators.AbstractButtonOperator.changeSelectionNoBlock(boolean)
org.netbeans.jemmy.operators.JMenuBarOperator.pushMenuNoBlock(ComponentChooser[])
org.netbeans.jemmy.operators.JMenuBarOperator.pushMenuNoBlock(String[])
org.netbeans.jemmy.operators.JMenuBarOperator.pushMenuNoBlock(String path, String delim)
org.netbeans.jemmy.operators.JMenuOperator.pushMenuNoBlock(ComponentChooser[])
org.netbeans.jemmy.operators.JMenuOperator.pushMenuNoBlock(String[])
org.netbeans.jemmy.operators.JMenuOperator.pushMenuNoBlock(String, String)
org.netbeans.jemmy.operators.JPopupMenuOperator.pushMenuNoBlock(ComponentChooser[])
org.netbeans.jemmy.operators.JPopupMenuOperator.pushMenuNoBlock(String[])
org.netbeans.jemmy.operators.JPopupMenuOperator.pushMenuNoBlock(String, String)
Examples:
WaitDialogSample.java,
ModalDialogSample.java
Note:
Indeed, any blocking action should be just executed in
a separate thread. So if you have a testcase where a modal dialog
is shown, but Jemmy does not provide such kind of nonblocking operation,
you will have to create the thread yourself. Either way is good, but we
recommend to override the org.netbeans.jemmy.operators.Operator.NoBlockingAction
class and run it using the
org.netbeans.jemmy.operators.Operator.produceNoBlocking(NoBlockingAction,Object)
or the org.netbeans.jemmy.operators.Operator.produceNoBlocking(NoBlockingAction)
method.
Using the Event queue
Since all GUI updates normally go though the event queue, it can be
used for the test stabilization purpose. Read
"EventQueue using"
for more information.
Example:
QueueUsingSample.java
Note:
If, during execution, the application does not update its GUI periodicially,
then the GUI may only change after some user input. So,
if the queue is empty, we can be sure that user input has
been processed completely, and thus, we can post other input operations.
If the application does run something, we cannot be sure of this.
Note:
org.netbeans.jemmy.QueueTool class provides some
more functionality such as wait for the queue be staying empty during certain
time.
Jemmy exceptions
Any unsuccessful operation in Jemmy exits with an exception. All exception
classes extend
org.netbeans.jemmy.JemmyException.
Note:
Here's a tip on how to use Jemmy functionality in a negative
way. You could insert an operation which shouldn't work in a try-catch
block.
String resources
String resources like button text, window titles, tree paths, ... could
be moved into special resource files.
It saves you from changing all the tests in case just the application's
window title was changed. Also it requires just one set of tests for all
languages supported by the application.
Examples:
ResourceSample.java,
resourcesample.txt
You can load resources from a file by using the
org.netbeans.jemmy.Bundle.loadFromFile(String)
method.
Note:
Implementation is in two classes: org.netbeans.jemmy.Bundle
and org.netbeans.jemmy.BundleManager
Drivers
This is definitely low-level functionality, and normally it's not something
test developer should take care about.
Starting from Jemmy 2.0 operators behavior is defined by a set
of classes called "drivers" (
org.netbeans.jemmy.drivers package).
For instance, button puching is performed by
ButtonDriver implementation,
which has
press,
release and
push methods.
The implementation could do pushing using mouse, keyboard, or just invoke
AbstractButton.doClick() method.
By default Jemmy uses a set of drivers allowing it to work exactly the same way
Jemmy 1.0 working.
Practically, depending on you specific system requarements you might
want to change some of the existing drivers to perform user actions reproducing
model. You can even create you own drivers - just implement one of the
*Driver
interfaces and register you driver by one of the
DriverManager methods.
Do this registration before any other test action so all the created operators will use
registered drivers.
Timeouts
All GUI operations take time, so a test running in it's own thread,
mostly waits for something to happened.
Jemmy has a lot of timeouts for different types of waiting. All timeouts
used by a class are usually described in javadoc (you can see them in the
class' page title).
Note:
If waiting was not finished successfully in an appropriate
time, org.netbeans.jemmy.TimeoutExpiredException will be thrown.
There also is another type of timeout - sleeping timeouts. For different
purposes (like demo or test debugging) it's useful sometimes to increase
them.
Default values for all sleeping timeouts are 0, except Waiter.TimeDelta
(see below)
Examples of timeouts:
ComponentOperator.PushKeyTimeout - time to sleep between key pressing
and releasing.
Waiter.TimeDelta - default time to sleep between attempts.
WindowWaiter.WaitWindowTimeout - maximum time to wait window.
JTreeOperator.WaitNodeExpandedTimeout - maximum time to wait next node
loaded during tree operations
Timeouts values can be stored in file in properties format:
Waiter.TimeDelta=1
WindowWaiter.WaitWindowTimeout=180000
You can load them using the
org.netbeans.jemmy.Timeouts.load(String)
method.
Robot vs event dispatching
Starting from 1.3, Java has included the
java.awt.Robot class
which generates native system input events for the purposes of test automation.
Jemmy test can be executed using Robot, as well as without. In the second
case, Jemmy dispatches events directly to components working with.
So, what's the difference?
Obviously, Robot mode is more "honest." If something cannot be done
manually, it cannot be done automatically. This gives us a certainty the
only tests that passed are the ones that can be reproduced manually. And
there is no such thing as, what cannot be done by robot mode can only be
done manually.
On the other hand, "dispatching" mode does not care about any window
could be opened by the window manager even if it completely closes the
application's windows. It can work on locked display (it depends on operating
system and window manager). It is also a little faster.
"Dispatching" mode is used by default. You can change it this way:
JemmyProperties.setCurrentDispatchingMode(JemmyProperties.ROBOT_MODEL_MASK)
Or from command line:
java ... -Djemmy.robot_dispatching=on ...
Timeouts and resources location.
Jemmy recognize one more option from command line:
jemmy.properties.
Value of this option must be file name containing two (or just one of them)
strings:
TIMEOUTS_FILE=[file containing timeouts]
RESOURCE_FILE=[file containing string resources]
These string defines files to load
timeouts
and
string resources resources from.
Test execution
Tests are usually executed using a test harness program which provides
execution by a list of test, test monitoring, tests result formatting,
and so on. Use your harness documentation to find out how to organize your
tests. If it can implement org.netbeans.jemmy.Scenario interface, though, it
can be executed from command line directly:
java -classpath [jemmy classes]:[test classes]:[application classes]
[aplication options] [jemmy options] org.netbeans.jemmy.Test [test class] [test
parameters]
Jemmy options could be:
jemmy.properties,
jemmy.queue_dispatching
and
jemmy.robot_dispatching
Test stability
GUI tests are unstable - that's a reality.
Imagine if the network was very busy when a test was waiting for the
window which loads something through the network during creation.
How long should the test wait for the window? How soon it should give
up?
We have a choice here.
We can set the window waiting timeout awfully long. In this case, the
testsuite won't completely pass because of an application or test bug in
the very first test.
Or we can use some reasonable value and check test failures after each
testsuite execution.
It's up to you which way to choose. It depends on a lot of things:
how big your testsuite is, how stable application design is and so on.
Note:
Jemmy provides some useful APIs to find the cause for
a failure. It is: org.netbeans.jemmy.util.Dumper class which dumps
information about all displayed components into a file in XML format. And
the org.netbeans.jemmy.util.PNGEncoder class grabs information from
the screen and puts it into a PNG image file.