• Ubuntu hardy with the default gnome desktop installed.
  • Install the python-dogtail package from universe
  • Enable accessibility features:
    • System -> Preferences -> Universal Access -> Assistive Technology then tick "Enable assistive technologies"

    • Log out and back in. The accessibility features won't be enabled until after you do this.

Getting started

  • Create a project directory such as udtp (ubuntu desktop testing project)

  • Save the sample script to udtp and run it with "python".

This script will open gedit, enter "hello, world" in the edit window and save it as hello.txt. Check for the existence of this file to confirm that the script has worked correctly.

Recording scripts

Dogtail ships with a script recorder that allows you to quickly record test cases, saving them in dogtail-python format. These will often need to be edited by hand afterwards to craft more useful test scripts but the script recorder is a great way of generating the bulk of the script.

  • Open the script recorder:
    • Applications -> Programming -> Dogtail script recorder

  • Open the application you want to record a script for (e.g. the gnome calculator). The application must have accessibility support for this to work, which is currently only the case for GTK applications (including GTK-enabled OpenOffice) Firefox 3 has some access support.

  • Click the Record button on the script recorder

  • Click on the application you are testing to give it focus
  • Use the mouse and keyboard to perform actions in the application you want to test. You can open, edit and save files. Some actions such as drag-and-drop will not record properly.
  • When you've completed the recording click Stop followed by Save.

    • Save the script as a python code file in your project directory

Tip: Give descriptive names to your test scripts, including the name of the application being tested. e.g. gcalctool-adding-test-1.0


To play back the script you have just recorded, simply click the Play button on the recording GUI. If the playback fails at any stage you should run the test script from the command line to get further information about the failure:

cd udtp
python ./gcalctool-simple-test-1.0

Failures in playback occur quite frequently and are often caused by the test system itself (including the system slow-down it causes) but may also represent a shortcoming in the accessibility support of the application being tested. The former can often be worked around by structuring the test differently or inserting pause statements.


Next we consider two recording examples, one which works as expected and one which gives an error when played back (on Hardy at time of writing)

The following screen shot shows a recording of the act of changing the view mode of the calculator from Basic to Advanced.


Changing view mode of the calculator

The script records and plays back as expected both in the GUI and on the command line.

In the next example we try adding 1 and 1 with the calculator. It records fine, but fails to play back. In the GUI ('Play' button) it just fails silently while on the command line it produces useful errors we can use to start debugging.


A simple recording of adding 1 and 1


TBD: write a more in-depth description on how to debug scripts that fail, including filing bugs against the test system (dogtail) and the application being tested. Also describe work-arounds to get the script running, like inserting timeouts or using keyboard shortcuts.

Modifying recorded scripts

Beyond just getting the recorded script to work you might also wish to modify it to add functionality that is not possible to record. This may include adding loops for stress testing or combining several recorded snippets to produce longer tests. [write more detail on this]

The dogtail API

You may want to read /usr/share/python-support/python-dogtail/dogtail/ to get a feeling for the API, though it is somewhat tricky Python code.

Some notes on the dogtail API:

  • run("programname") runs a GUI program and waits until it has opened a window.
  • focus.application("windowname") chooses a top-level. Despite the name, this does not actually change the focus in the application, just a pointer inside the dogtail library.
  • focus.text() chooses a text editing widget.
  • There are a bunch of other such helper functions, see for info.
  • focus(name="foo", roleName="bar", description="foobar") is the real way to choose widgets, which the above helpers call, in most cases (though not all). You get the name, roleName, and description values from dogtail-sniff (see below). You only need to specify the one or ones you want to, but you need at least one.
  • In all cases, when choosing a widget, dogtail uses heuristics. Sometimes this fails. For example, Gedit has a widget called "Save" in at least two places: the toolbar in the main window, plus a button in the "Save as..." dialog. To work around this, you may need to choose a parent widget first: dogtail seems to prefer to find children of the currently chosen widget.
  • click("name") performs the "click" action on a widget, which is first chosen; arguments are same as for focus(). Clicking does not work for widgets, but it does for work those for which it is natural, such as buttons.
  • click("name") also seems to work for menu items.
  • type("text") pretends the user is typing the given text.

Additional notes:

  • dogtail-sniff lets you browse the widgets of other, running programs. This is how you figure out the names to give to the API to search for them. Note that the names must be exact, and that copy-paste is sometimes the only reliable way of getting them right (especially with silly Unicode ellipsis characters, as in the example below).
  • When you click on a widget in dogtail-sniff, it gets briefly highlighted in the actual window. Be prepared, or you'll miss it.
  • The API also provides a dump functions that lets you see the tree of nodes available on the application, example (you need to start the application first):

%> python
>>> from dogtail import tree
>>> gcalctool = tree.root.application('gcalctool')
>>> gcacltool.dump()

You'll see something like:

{"gcalctool" application}
 Node roleName='frame' name='Calculator  - Scientific' description=''
  Node roleName='filler' name='' description=''
   Node roleName='menu bar' name='' description=''
    Node roleName='menu' name='Calculator' description='' text='Calculator'
     Node roleName='tear off menu item' name='' description='' text=''
     Node roleName='menu item' name='Quit' description='' text='Quit'
     Node roleName='menu item' name='Empty' description='' text='Empty'
    Node roleName='menu' name='Edit' description='' text='Edit'

Sample program to do a simple Gedit test

Save this as as

# coding=utf-8

# A small sample script for dogtail. Starts Gedit, inserts some text,
# saves it, quits Gedit, and verifies that the saved file is correct.

import os
import shutil
import tempfile

from dogtail.procedural import *

os.environ["LC_ALL"] = "C"

dirname = tempfile.mkdtemp()
filename = os.path.join(dirname, "hello.txt")
string = "hello, world"



focus.widget(name="Save As…", roleName="dialog")
# At this point we have what is technically called a race condition.
# The save might not have happened by the time click.button() returns,
# and so we might be quitting too early. On my desktop machine it is
# fast enough, inside qemu not.


saved_string = file(filename).read()
if saved_string != string + "\n": # gedit adds a newline to the end, always!
    print "error: bad content saved"
    print repr(saved_string)
    print "ok"


Testing/Automation/DogtailTutorial (last edited 2008-08-06 17:01:14 by localhost)