Testing GUIs with TextTest and StoryText
Using StoryText with SWT/Eclipse RCP/GEF
Supported platforms
It is tested regularly and works fully on Linux and Windows. It currently does not work under Cocoa on the Mac: the reason is that the SWTBot headless test runner, on which it depends, runs the UI in a non-main thread which Cocoa does not support. The suggested workaround from the SWTBot project is to use Carbon instead. However, there are also serious GTK bugs for the Mac, which will hinder the use of TextTest and the StoryText editor, so in general, Mac support is not in a very good place right now.
GEF support is maturing in version 3.8 but is still less mature than ordinary SWT/Eclipse RCP support.
Installing StoryText and Jython
On Windows, you should install Java if you haven't already done so, and then run the Windows installer from the sourceforge page. Note you will need to be connected to the internet while running this installer. This installer will also install TextTest.
On Linux, you probably already have Python and PyGTK, so you will need to do the following:
  1. Install Jython.
  2. Download and unpack the tarball from the sourceforge page
  3. From its source directory, run first "python setup.py install" and then "jython setup.py install"
  4. Ensure the bin directory of both your installations created above is added to your PATH, with the Jython one coming first.
  5. Test this from the command line: "which storytext" should return the one in your Jython installation, while "which storytext_editor" should return the one in your Python installation.
(You can also use "pip" to install StoryText directly from PyPI without downloading the tarball, but then you have to install pip under Jython or use "virtualenv" to create a separate jython environment containing "pip". Note also that earlier restrictions in StoryText to use Jython 2.5.1 with RCP applications have now been lifted, the latest Jython release should work just fine since 3.11)
Installing SWTBot
StoryText support for SWT and Eclipse RCP is based on the tool SWTBot which you will also need to install. For plain SWT it is sufficient to install it "normally", i.e. as recommended by the SWTBot project on their site. Installing it as for RCP (below) will also work.
For Eclipse RCP apps, it is necessary to install an additional "bridge" module so StoryText and SWTBot can work together. This is known as org.eclipse.swtbot.testscript, but is currently maintained by the StoryText project (though in theory it could be used by anyone attempting to use a dynamic language with SWTBot). The recommended way to do this is to use the "p2 director" to install it under your product. For Eclipse 3.6, use a command as shown below. (NOTE! In reality, do not use linebreaks between the different repositories, it is formatted like this below just to prevent an excessively wide display.)
eclipse \
-application org.eclipse.equinox.p2.director \
-repository http://texttest.carmen.se/swtbot_testscript/3.6,
http://download.eclipse.org/eclipse/updates/3.6 \
-installIU org.eclipse.swtbot.testscript \
-consoleLog \
-debug \
-destination <path_to_your_product> \
-profile profile \
For newer versions of Eclipse, please adjust the above command to the versions. We have no 3.7 repository for the "testscript" module, but the 3.6 one should work. Note that "helios" in the second repository needs to be switched for e.g. "indigo". For Eclipse 3.5, use the same command, except for the repository paths
-repository http://texttest.carmen.se/swtbot_testscript/3.5,
http://download.eclipse.org/eclipse/updates/3.5 \
Usage with GEF applications (from StoryText 3.7)
Installation should be as above, except the "-installIU" argument needs to be changed so that it uses "org.eclipse.swtbot.gef.testscript" instead, and a suitable GEF repository provided. I.e. something like this:
eclipse \
-application org.eclipse.equinox.p2.director \
-repository http://texttest.carmen.se/swtbot_testscript/3.6,
http://download.eclipse.org/eclipse/updates/3.6 \
-installIU org.eclipse.swtbot.gef.testscript \
-consoleLog \
-debug \
-destination <path_to_your_product> \
-profile profile \

It's reasonably likely that you will need to write your own customwidgetevents.py (see section on custom widgets) to be able to use StoryText effectively on a GEF application. The reason is that the GEF EditParts (figures) are by their nature hard to describe and index in a good way generically. For example the drag+drop support will record pixel positions and it's likely you'll want to tweak this to be able to record and playback something from your domain.
Supported operations over and above the normal SWT ones are essentially selecting the figures and dragging and dropping them, and of course describing their appearance (including coloured regions etc). Also using context menus attached to them should work.
Widget Naming
StoryText has various ways to identify widgets in an SWT app. These are, in order of preference
  • The text on the widget, if it isn't editable
  • The text on any Label immediately preceding the widget in its parent container
  • The tooltip on the widget
  • The title of the dialog where the widget appears
  • The ID of the JFace Action associated with the widget, if any (the result of widget.getData().getAction().getId())
  • The ID of the View in which the widget appears (RCP only)
  • The type of the widget (class name)
The hope is to find a unique way of identifying the widget based on this information, so that it can be referenced in the UI map file. There will however exist some cases where this isn't sufficient to identify a widget, for example if the widget is only identified by type, or the text isn't unique, or it changes depending on e.g. today's date. In these cases the widget needs to be named explicitly in the code, and it's fair to say that most non-trivial applications will need to name at least some widgets before StoryText will work smoothely.
The mechanism for this is inherited from SWTBot, and works in the same way as if you were using that tool alone. You simply add a call like this in your code:
widget.setData("org.eclipse.swtbot.widget.key", "My Widget");
Variations of replayer events
For entering text into widgets, the instruction "Modify" is normally recorded into the UI map file. When replayed this will "set" the text directly, which is similar to pasting it in with the mouse.
Sometimes you actually want to use the keyboard to type text in directly, for example to trigger responses in the application that only apply if keypresses are actually detected. In this case you can find the relevant entries in the UI map file and manually change "Modify" to "Modify.typed". (Note that this can lead to trouble with keyboard layouts and is often not a good idea if you need to type "unusual" characters.)
In a similar way you can change "ActivatePart" to "ActivatePart.clicked" when activating views in an RCP application. Normally this is done programmatically, but you can change it to actually seize the mouse pointer and click the mouse on the relevant tab. Sometimes this distinction can be important.
Application events
(See here for an explanation of what an Application Event is: the basic idea is to support synchronisation by recording and reading of "waits")>
From a Java app, it isn't very easy to import a Python module and call the code there directly, so there is a different interface to the one described here for the Python toolkits. The basic idea is to do as follows:
Event event = new Event();
event.text = "completion of background process";
widget.notifyListeners(1234, event);
"widget" here can be any widget at all. "1234" is just a convention to avoid colliding with real Eclipse RCP events.
The name provided in "event.text" will then be placed after "wait for " in the usecase files when the event is recorded, so choose something that makes sense in that context, e.g. "data to be loaded".
If several such events follow each other, they will overwrite each other, i.e. only the last one will be recorded. To avoid this, you can set a category for the event, by inserting a line like
event.data = "background category";
in which case only events in the same category will overwrite each other, while those in different categories will be treated as independent and StoryText will wait for all, in any order, before proceeding. Application event categories are discussed more on the general page for application events.
Jobs in Eclipse RCP
Eclipse has its own "job" mechanism defined in the package "org.eclipse.core.runtime.jobs" and many background tasks use that mechanism. StoryText will in this case add its own job listeners and will generate application events automatically when a user-defined Job completes, so no instrumentation of the type described above is needed in this case.
By default StoryText will keep track of all jobs where isSystem() returns False. You can also set the environment variable "STORYTEXT_SYSTEM_JOB_NAMES", to tell it which system jobs should be monitored also.

Last updated: 23 September 2014