About
You’ve written an application in Python using PySide and Qt. You want to automate testing. For example, you are using agile development and want to test all features of your app after you make any change to the app. You want to test at the functional level, that is, from a user’s perspective. What tools do you use?
This blog posting is a true log: a sequence of actions that I took in exploring the above question. You probably don’t want to follow it exactly; there is much thrashing. It is a ramble in dependency weeds.
Exploring frameworks for functional testing
A little search finds the usual suspects:
Criteria for selecting a framework for functional testing
- cost
- adaptability (open sourcedness)
- script writing versus recording
- cross platform support
My evaluation of frameworks for functional testing Qt apps
Storytext is open source and free but doesn’t support Qt. It is a recorder, and does have a novel idea of adding a layer of abstraction between tests and the SUT widgets: if your GUI changes, you may not need to re-record tests. (I actually started to adapt StoryText to Qt, but didn’t get very far. I decided that an off-the-shelf framework might do 90% of what I needed.)
Squish is probably the best bet (if you can afford it), since it is commercially supported.
TestComplete seems to only run on Windows.
hooq is open source, but seems quiescent and has little documentation. A few blogs document experience with it. It is a recorder, so you don’t need to write test scripts.
TDriver is open source and was originally supported by Nokia and partners, but is now more or less orphaned. It does have some old documentation. It is not a recorder, but uses test scripts written in Ruby. There are a few blogs, such as this one about its use at Canonical to automate testing of Ubuntu apps. However, it is not apparent that Canonical is still using it. That seems strange; it seems like Ubuntu still lets contributors and (early adopting users) do most functional testing, at least for applications.
Testing whether a Python app can use the Qt testability plugin
Again, note that this is not necessarily doing things in the preferred order.
When a Qt app starts, you pass it command line args (also called options). If one of the args is ‘-testability’ then Qt tries to load a plugin (a dynamically loaded library) to support testing.
>./pensool.py -testability
yielded the error message ‘Library qttestability load failed’ on the console.
Building the agent
I read these instructions for installing TDriver for Meego. There, you find instructions for installing TDriver from repositories. However, it points to repositories for Ubuntu 10.10. These are probably dated. Meego itself seems to be an orphaned, unsupported project. So I decide to follow the instructions for “building on other platforms”, that is, build from source for newer platforms. Alternatively, there are other instructions.
I think the missing library is part of the so-called agent. The architecture of the Qt testability framework allows for the test runner to be on a host and for the system under test (the SUT) to be for example a cell phone, and the two commuicate via an agent. Here I am running everything on the same machine, but I still need the agent. To build the latest version of the agent:
git clone http://git.gitorious.org/tdriver/agent_qt.git cd agent_qt qmake -r CONFIG+=no_mobility make sudo make install
(Without the ‘no_mobility’ you will get an error about ‘mobility API.’)
I also got an error about an include file ‘X11/extensions/XTest.h’.
Getting the XTest package
This is about the X11 windowing system on Linux. One extension called XTest supports sending X events to an app for testing. The extension is in a package ‘libxtst-dev’. Note the spelling is different. Note that ‘-dev’ as always means it include source and header files for compiling other programs with it. I found and installed the package using the Ubuntu Software Center app (or > sudo apt-get install libxtst-dev’ would work.)
Now, the build of the agent works, and starting my SUT works without an error about missing library qttestability.
Architecture of TDriver
You start three components:
- your app with the -testability option
- the agent ( a server to a test runner or the Visualizer) : > qttasserver
- a test runner or Visualizer: > tdriver_visualizer
You can start them in separate consoles (or all in the same console with the ‘&’ suffix to start them in the background, but then their printed output to the console, if any, is intermixed.)
Building the Visualizer app
The Visualizer is a graphical app that shows you the widget structure of a running Qt app. It is not necessary for testing (TDriver test cases are written in Ruby scripts.) But I wanted to test whether the Visualizer would connect to my app.
git clone http://git.gitorious.org/tdriver/visualizer.git cd visualizer qmake -r make sudo make install
Trying the Visualizer app
The Visualizer is a graphical app that shows you the widget structure of a running Qt app. It is not necessary for testing (TDriver test cases are written in Ruby scripts.) But I wanted to test whether the Visualizer would connect to my app.
The app has a different command line name:
>tdriver_visualizer
I got an error message: Could not locate TDriver parameters file: /etc/tdriver//tdriver_parameters.xml
A little research shows that probably I first need to install the Ruby components, which probably will do the configuration.
I read these instructions for installing TDriver for Meego
Installing needed Ubuntu packages
sudo apt-get install rubygems ruby1.8-dev libxslt-dev libxml2-dev
What is rubygems?
rubygems is a package that lets you download and install packages written in the Ruby language. It is similar to Python’s disutils package, but it seems to download packages, not just install them (like >python setup.py install.)
You could also install it:
Start the ‘Ubuntu Software Center’ application (click on the icon in the launcher at the left side of your display.)
Search for ‘rubygems’. You should find a package named ‘rubygems’. Select it and click on the ‘Install’ button.
Now you should have the command line application: gem.
Installing Ruby libraries
sudo gem install testability-driver testability-driver-qt-sut-plugin
This downloads, builds, and installs the libraries, written in Ruby, for TDriver. I don’t know where gem looks for these, I assume it is not in the gitorious repository for the rest of TDriver. I’m not sure whether these libraries are more general purpose, that is, whether they are useful for other frameworks than Qt.
If you failed to install the package libxslt-dev, you will get errors.
I also got the error “ERROR: While generating documentation for builder-3.2.0” but I assume this is not critical, it is the documentation.
Finally a modicum of success
Now I started my app, the agent, and the visualizer, without error messages. I futzed with the visualizer until it showed my app. I discovered what I feared: it doesn’t show much. It shows some of my Qt widgets and even QGraphicItems, but most of them are unnamed. That is probably something I can change.
Also, my app implements some of its own widgets (controls) that don’t inherit from QWidget, and they are missing from the visualizer. Maybe I need to rethink that design.
Finally, I used QContextMenus, but they are hidden and don’t show up in the visualizer. Maybe I can still send them events. But their action depends on the QGraphicItems they hover/pop-up on, and I don’t want to hardcode coordinates into my tests.
At best, I may be able to test part of my app: all the menubar items (e.g. File>Open). Possibly I can test all the context menu items too. I probably won’t be able to test some of the esoteric GUI techniques, such as hover tools.
Now to writing some test scripts…
A test script in Ruby
I hacked an example from the links above:
#!/usr/bin/env ruby require 'tdriver' include TDriverVerify sut = TDriver.sut(:sut_qt) pensool_app = sut.run(:name => '<fullpath>/pensoolQt/Foo') pensool_app.Button(:text => '1').tap verify { pensool_app.QLineEdit(:text => '3') } pensool_app.close
The first goal is just to start the app successfully. When I executed the above file, I got a warning similar “required file not found: tdriver”. This means that Ruby is not finding its libraries (gems). To solve this, define an environment variable in your shell session before executing the test script (or define it permanently in your .profile):
>export RUBYOPT=rubygems
(Coming from the Python world, I am surprised. A default Ruby installation needs to be told to use rubygems? I was under the impression that Ruby had a better library management system than Python.)
Now when I run the script, it waits a minute then I get:
/var/lib/gems/1.8/gems/testability-driver-1.5.4/lib/tdriver/base/sut/generic/behaviours/sut.rb:986:in `__wrappee_316__run’: Run failed. Failed to launch application. Exception: MobyBase::ApplicationNotAvailableError (MobyBase::BehaviourError)
So I open that file and start reading. Apparently the test framework is trying to communicate with the SUT and matches it by application name. I start the visualizer and see that the SUT has various attributes: applicationName=Pensool but fullName=/usr/bin/python2.7. So I suspect that the framework is not ready for an interpreted SUT, where the ‘name’ of the executable is the interpreter (python2.7) which doesn’t match the name of the script file (Foo) which doesn’t match the name that my app shows to users (Pensool.)
Aaaargh, more later…..
Foo