diff options
Diffstat (limited to 'old/doc/src/qtuitest_plugins.qdoc')
-rw-r--r-- | old/doc/src/qtuitest_plugins.qdoc | 297 |
1 files changed, 297 insertions, 0 deletions
diff --git a/old/doc/src/qtuitest_plugins.qdoc b/old/doc/src/qtuitest_plugins.qdoc new file mode 100644 index 0000000..d3161d3 --- /dev/null +++ b/old/doc/src/qtuitest_plugins.qdoc @@ -0,0 +1,297 @@ +/*! + +\page qtuitest_plugins.html +\title Creating a QtUiTest Test Widget + +\tableofcontents + +\section1 Introduction + +QtUiTest provides support for simulating high-level user interface interactions. +Conceptually these take the form of, for example, \i{Select 'Dog' from the widget labelled 'Animal'}, or \i{Enter 'Bob' into the widget labelled 'Name'}. + +Crucially, testcases do not need to specify the exact type of each widget or how to interact with it. +Instead the logic for interacting with different types of widgets resides on the system under test, either alongside the implementation of each widget or in a plugin. +Each class of widget which QtUiTest can interact with has a corresponding wrapper class, referred to as a test widget. + +This document explains when a test widget must be created and how it is implemented. + +\section1 When a Test Widget is Required + +QtUiTest includes support for most Qt widgets. When a new type of widget is introduced, a test widget may be required. + +In these cases, a test widget will almost certainly have to be implemented: +\list +\o A custom widget implements painting and interaction by overriding functions such as \l{QWidget::}{paintEvent()} and \l{QWidget::}{keyPressEvent()}. +\o The behavior of a major user interface element has been customized (for example, a device profile is used which has a device-specific way of accepting incoming phone calls). +\endlist + +In these cases, a test widget will usually not be required: +\list +\o A custom widget acts primarily as a container for standard widgets. + In this case, QtUiTest can interact with the contained widgets as normal. +\o A custom widget subclasses a standard widget and correctly reimplements virtual functions. + For example, a subclass of QAbstractItemView which performs custom painting with a custom model will work with no additional effort if \l{QAbstractItemView::}{visualRect()} is implemented correctly (for mouse interaction), standard item view key event handling is used and the item model accurately reports its data. +\endlist + +As an example, consider a testcase for creating a contact in the addressbook application. +At one point in the testcase, we wish to set the contact's title to Doctor: + +\c{select("Dr", "Title");} + +This requires that the widget referred to by "Title" implements the \l{QtUiTest::}{SelectWidget} interface. +If this is not the case, the testcase fails with a failure message like the following: + +\c{FAIL! : sys_addressbook::creating_a_contact() ERROR: Title (QComboBox(0x80b2768)) is not a SelectWidget.} + +This error message indicates that the "Title" widget, which is a QComboBox, does not have any corresponding test widget which implements the \l{QtUiTest::}{SelectWidget} interface, and therefore can't have items selected from it. + +Note that not all errors arising from missing test widgets will be of this form. + +\section1 Choosing the Right Interfaces + +Test widgets must implement one or more of a standard set of interfaces belonging to the \l{QtUiTest} namespace. + +Test widget interfaces map to the conceptual purpose of a widget from a user's perspective. +The available interfaces are listed below: + +\table +\header \o Interface \o Applies to \o Examples +\row +\o \l{QtUiTest::}{Widget} +\o All 2D user interface elements. +\o QWidget +\row +\o \l{QtUiTest::}{ActivateWidget} +\o Widgets which are activated to trigger some action. +\o QAbstractButton +\row +\o \l{QtUiTest::}{CheckWidget} +\o Widgets which can be checked and unchecked. +\o QCheckBox, QRadioButton +\row +\o \l{QtUiTest::}{TextWidget} +\o Widgets which display text. +\o QLineEdit, QTextEdit, QMenu, QAbstractItemView, QLabel, many others +\row +\o \l{QtUiTest::}{InputWidget} +\o Widgets which can accept text input. +\o QLineEdit, QTextEdit +\row +\o \l{QtUiTest::}{ListWidget} +\o Widgets which display a list of items. +\o QAbstractItemView, QMenu +\row +\o \l{QtUiTest::}{SelectWidget} +\o Widgets which allow an item to be selected from a list. +\o QAbstractItemView, QMenu +\endtable + +Each test widget should implement all interfaces applicable to the wrapped widget. +Test widgets can subclass other test widgets and reuse already-implemented interfaces. +For example, the test widget for QCheckBox could inherit from the test widget for QAbstractButton to avoid having to reimplementing the \l{QtUiTest::}{Widget}, \l{QtUiTest::}{TextWidget} and \l{QtUiTest::}{ActivateWidget} interfaces again. + +Some test widget interfaces are strongly related and are likely to be implemented in pairs. +Almost all widgets which accept text input also display the entered text, so any test widget which implements \l{QtUiTest::}{InputWidget} will usually implement \l{QtUiTest::}{TextWidget}. +Almost all widgets which can be selected from also display the selectable items, so any test widget which implements \l{QtUiTest::}{SelectWidget} will usually implement \l{QtUiTest::}{ListWidget}. + +\section1 Implementing the Test Widget + +To make a new test widget visible to QtUiTest, there are two separate approaches which can be taken, each with their own advantages. + +\section2 Plugin method + +The plugin method involves adding the test widget code into a plugin which is then loaded by QtUiTest at runtime. +This is the most suitable method to use in most cases and the only method used for the test widgets shipped with QtUiTest. + +Advantages to the plugin method compared to the multiple inheritance method include: +\list +\o The code for the test widget is cleanly separated from the wrapped widget and hence easy to omit from a release build without the need for \c{#ifdef}s or similar measures. +\o It is easier to reuse test widget code because test widgets aren't directly tied to wrapped widgets. +\o It is possible to customize the process of \l{QtUiTest::WidgetFactory::find()}{finding} and \l{QtUiTest::WidgetFactory::create()}{creating} test widgets. +\o Typical multiple inheritance difficulties are avoided, such as the test widget interface API shadowing the API of the wrapped widget. +\endlist + +\section3 Test widget class + +Each test widget class needs to inherit from QObject and the relevant test widget interfaces. + +In practice, it is common for a test widget class hierarchy to be written which closely mirrors the wrapped widget class hierarchy. +This makes it possible to avoid rewriting the code for common interfaces such as \l{QtUiTest::Widget} many times. + +It is possible to subclass the test widgets shipped with QtUiTest, although they are not guaranteed to remain source or binary compatible across releases. +The convention used in the reference plugins to generate a test widget class name is to take the wrapped widget class name, drop any leading Q, and prefix Test. +For example, the test widget wrappers for QWidget and QAbstractItemView are named TestWidget and TestAbstractItemView respectively. + +Using the plugin approach, while subclassing from TestWidget to avoid reimplementing the \l{QtUiTest::Widget} interface, would result in a class declaration like the following: + +\code +#include <testwidget.h> +class TestCustomComboBox : public TestWidget, public QtUiTest::ListWidget, + public QtUiTest::SelectWidget, public QtUiTest::TextWidget +{ +Q_OBJECT +Q_INTERFACES( + QtUiTest::ListWidget + QtUiTest::SelectWidget + QtUiTest::TextWidget) + +public: + TestCustomComboBox(CustomComboBox* wrapped); + virtual ~TestCustomComboBox(); + + // QtUiTest::ListWidget members + virtual QStringList list() const; + virtual QRect visualRect(const QString&) const; + + // QtUiTest::SelectWidget members + virtual bool canSelect(QString const&) const; + virtual bool select(QString const&); + + // QtUiTest::TextWidget members + virtual QString text() const; + virtual QString selectedText() const; + +private: + CustomComboBox* m_wrapped; +}; +\endcode + +Implementing the test widget is as simple as retrieving the necessary information from the wrapped widget. +Test widgets can create and use other test widgets at runtime when necessary, as shown in the \c{list()} function below. + +\code +TestCustomComboBox::TestCustomComboBox(CustomComboBox* wrapped) + : m_wrapped(wrapped) +{} + +QStringList TestCustomComboBox::list() const +{ return qtuitest_cast<QtUiTest::ListWidget*>(m_wrapped->view())->list(); } + +QString TestCustomComboBox::text() const +{ return list().join("\n"); } + +QString TestCustomComboBox::selectedText() const +{ return m_wrapped->currentText(); } +\endcode + +Memory management is handled automatically; there will be a maximum of one TestCustomComboBox instance created for any CustomComboBox and it will be destroyed when the underlying CustomComboBox is destroyed. + +\section3 Test widget factory + +When using the plugin approach it is also necessary to implement a factory class. +This serves as the entry point to the plugin and handles the logic for creating test widgets. + +The test widget factory must subclass \l{QtUiTest::WidgetFactory} and implement the \l{QtUiTest::WidgetFactory::}{create()} function to create test widgets and the \l{QtUiTest::WidgetFactory::}{keys()} function to report which widget classes can be wrapped. + +\code +class TestWidgetsFactory : public QObject, public QtUiTest::WidgetFactory +{ + Q_OBJECT + Q_INTERFACES(QtUiTest::WidgetFactory) + +public: + TestWidgetsFactory(); + + virtual QObject* create(QObject*); + virtual QStringList keys() const; +}; +\endcode + +The \l{QtUiTest::WidgetFactory::}{create()} function is called when a new test widget is to be created. +Our example widget factory handles CustomComboBox widgets and nothing else. + +\code +QObject* TestWidgetsFactory::create(QObject* wrapped) +{ + if ((CustomComboBox* ccb = qobject_cast<CustomComboBox*>(wrapped))) { + return new TestCustomComboBox(ccb); + } + return 0; +} +\endcode + +The \l{QtUiTest::WidgetFactory::}{keys()} function must report which classes can be handled by this factory. +Any object passed into the \l{QtUiTest::WidgetFactory::}{create()} function is guaranteed to be one of the classes returned by \l{QtUiTest::WidgetFactory::}{keys()}. +Classes are handled from most to least specific; when creating a test widget for CustomComboBox, a factory which handles CustomComboBox has higher priority over a factory which handles QWidget. +If two or more factories handle the same class, it is undefined which factory will be asked to create the test widget. + +Our example factory can only handle CustomComboBox widgets so it returns that class name only. +\code +QStringList TestWidgetsFactory::keys() const +{ + return QStringList() << "CustomComboBox"; +} +\endcode + +Finally, the plugin needs to be exported using the standard Qt plugin mechanism: +\code +#include <qplugin.h> +Q_EXPORT_PLUGIN2(customtestwidgets, TestWidgetsFactory) +\endcode + +In the project's \c{qbuild.pro}, the \c{PLUGIN_TYPE} must be set to \c{qtuitest_widgets}. + +\section2 Multiple inheritance method + +The multiple inheritance approach requires the widget being wrapped to implement the test widget interfaces itself. + +Advantages to the multiple inheritance method compared to the plugin method include: +\list +\o The code for the test widget is in the same file as the wrapped widget and hence is more likely to be maintained if the widget is modified. +\o The test widget has access to the internal structures of the wrapped widget. +\o There is no need to write a \l{QtUiTest::}{WidgetFactory} class to handle creation of test widgets. +\endlist + +For example, implementing a custom combobox class along with all of the associated test widget interfaces would result in a class declaration like: + +\code +class CustomComboBox : public QComboBox, public QtUiTest::Widget, + public QtUiTest::ListWidget, public QtUiTest::SelectWidget, + public QtUiTest::TextWidget +{ +Q_OBJECT +Q_INTERFACES( + QtUiTest::Widget + QtUiTest::ListWidget + QtUiTest::SelectWidget + QtUiTest::TextWidget) + +public: + CustomComboBox(QWidget* parent = 0); + ... + + // CustomComboBox members + void addCustomItem(const QVariant& item); + void addCustomItems(const QVariantList& items); + ... + + // QtUiTest::Widget members + virtual const QRect& geometry() const; + virtual QRect rect() const; + ... + + // QtUiTest::ListWidget members + virtual QStringList list() const; + virtual QRect visualRect(const QString&) const; + ... + + // etc... +}; +\endcode + +Implementing the test widget is as simple as returning the necessary information. +Test widgets can create and use other test widgets at runtime when necessary, as shown in the \c{list()} function below. + +\code +QStringList CustomComboBox::list() const +{ return qtuitest_cast<QtUiTest::ListWidget*>(view())->list(); } + +QString CustomComboBox::text() const +{ return list().join("\n"); } + +QString CustomComboBox::selectedText() const +{ return currentText(); } +\endcode + +*/ |