3.1+54.4
Implementing custom checks with the Checker
interface
Checks are one of QF-Test's most useful features. Test automation would be mostly
useless without the ability to verify the results of simulated actions. However, the
default set of Checks available in QF-Test is naturally limited to checking the most
common attributes of standard components. For special attributes or custom components
you can resort to read the value in an SUT script and use the
method rc.checkEqual()
to compare it against the expected value. Such an
SUT script is perfectly fine, it performs and integrates well, is flexible and
can be modularized by placing it inside a Procedure. It has two major
disadvantages however: It cannot be recorded and it is daunting for non-programmers.
With the help of the API described in this section the default set of checks in
QF-Test can be extended. In fact, QF-Test's own new-style checks are implemented exactly this
way. By implementing and registering a Checker
for a given type of GUI
element and possibly item you can create your own checks that can be recorded and
replayed just like the standard ones.
To make this as simple as possible, QF-Test handles everything from showing the check in
the check popup menu, fetching the check data, recording the respective Check node
to store that data, sending the data back to the SUT upon replay, fetching the then
current check data, comparing it to the expected value and reporting success or
mismatch. All that is left for you to do is tell QF-Test which checks your
Checker
implements and for each of these provide the check data on request.
Illustrative examples are provided at the end of the chapter and in the test suite
carconfigSwing_en.qft
, located in the directory demo/carconfigSwing
in
your QF-Test installation.
The Checker
interface
The interface de.qfs.apps.qftest.extensions.checks.Checker
must be
implemented in order to add custom checks for your application. The associated helper
classes and interfaces are documented in the subsequent sections.
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
The class Pair
The class de.qfs.lib.util.Pair
for the return value of
getCheckDataAndItem
is a simple utility class that often comes in handy
for grouping two values. You'll only need its constructor, but of course you can also
read its values:
|
|
||||||||||||||||||||||||||||||
The CheckType
interface and its implementation
DefaultCheckType
A de.qfs.apps.qftest.extensions.checks.CheckType
encapsulates information
for a specific kind of check. It combines a CheckDataType
with an
identifier and provides a user-friendly representation of the check for the check
popup menu. Unless you need to provide multi-lingual representations of the check you
should never implement this interface yourself, but simply instantiate a
de.qfs.apps.qftest.extensions.checks.DefaultCheckType
instead:
|
|
||||||||||||||||
For completeness sake, following are the methods of the CheckType
interface:
|
|
||||||||||||||||||||||||||
The class CheckDataType
The class de.qfs.apps.qftest.extensions.checks.CheckDataType
is similar
to an Enum
. It defines a number of constant CheckDataType
instances that simply serve to identify the kind of data that a check operates on.
Each constant corresponds to one or more of the available Check nodes of QF-Test.
Besides serving as a constant identifier, a CheckDataType
has no public
attributes or methods and you cannot add any new CheckDataTypes
. If you
want to implement a check of a kind that does not fit the existing data types you'll
need to convert your data so that it does, for example by using a string
representation. The following CheckDataType
constants are defined:
- STRING
- A single string. Used by the Check text node.
- STRING_LIST
- A list of string items, like the cells in a table column. Used by the Check items node.
- SELECTABLE_STRING_LIST
- A list of selectable string items, like the elements of a list. Used by the Check selectable items node.
- BOOLEAN
- A boolean state, either true of false. Used by the Boolean check node.
- GEOMETRY
- A set of four integer values for X and Y coordinates, width and height. Not all have to be defined. Used by the Check geometry node.
- IMAGE
- An image of a whole component or item or a sub-region thereof. Used by the Check image node.
The class CheckData
and its subclasses
The class de.qfs.apps.qftest.shared.data.check.CheckData
and its
subclasses, all from the same package, complete the Checker
API. A
CheckData
encapsulates the actual data for a check, must be returned from
Checker.getCheckData()
and is used to exchange this check data between
the SUT and QF-Test. There is one concrete CheckData
subclass corresponding
to each CheckDataType
. You'll only ever need to use their constructors,
so that's what we'll list here. Only two of these classes are publicly available so
far:
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Furthermore you can define an optional algorithm for an ImageCheckData.
|
|
||||||||
The CheckerRegistry
Once implemented and instantiated, your Checker
must be registered
with the CheckerRegistry
. The class
de.qfs.apps.qftest.extensions.checks.CheckerRegistry
has the following
interface:
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Custom checker example
The following Jython SUT script illustrates how to put everything together.
Let's say you have a Java Swing application and want to check all labels which
reside in a panel at once. To this end, your custom checker needs to iterate over
all components contained in the panel and its children respectively, identify the
labels and generate a list of all their text contents. In QF-Test notation, this means
you need to create a CheckDataType.STRING_LIST
check type and return
the data in an StringItemsCheckData
object:
from de.qfs.apps.qftest.extensions import ResolverRegistry from de.qfs.apps.qftest.extensions.checks import CheckerRegistry, \ Checker, DefaultCheckType, CheckDataType from de.qfs.apps.qftest.extensions.items import ItemRegistry from de.qfs.apps.qftest.shared.data.check import StringItemsCheckData from de.qfs.lib.util import Pair from java.lang import String import jarray componentClass = "javax.swing.JPanel" allLabelsCheckType = DefaultCheckType("AllLabels", CheckDataType.STRING_LIST, "All labels in the panel") class AllLabelsChecker(Checker): def __init__(self): pass def getSupportedCheckTypes(self, com, item): return jarray.array([allLabelsCheckType], DefaultCheckType) def getCheckData(self, com, item, checkType): if allLabelsCheckType.getIdentifier() == checkType.getIdentifier(): labels = self._findLabels(com) labels = map(lambda l: l.getText(), labels) values = jarray.array(labels, String) return StringItemsCheckData(checkType.getIdentifier(), values) return None def getCheckDataAndItem(self, com, item, checkType): data = self.getCheckData(com, item, checkType) if data is None: return None return Pair(data, None) def _findLabels(self, com, labels=None): if labels is None: labels = [] if ResolverRegistry.instance().isInstance(com, "javax.swing.JLabel"): labels.append(com) for c in com.getComponents(): self._findLabels(c, labels) return labels def unregister(): try: CheckerRegistry.instance().unregisterChecker( componentClass, allLabelsChecker) except: pass def register(): unregister() global allLabelsChecker allLabelsChecker = AllLabelsChecker() CheckerRegistry.instance().registerChecker( componentClass, allLabelsChecker) register()
After running that script once, you'll find a new entry "All labels in the panel"
among the entries in the check type menu as soon as you right click on a
JPanel
component while being in recording mode (cf. section 4.3). If you want to use the allLabelsChecker
all
over your client application, you can put the above SUT script behind your
Wait for client to connect node in the Setup sequence. Otherwise, you may register the
checker only when it is actually needed as shown above and remove it afterwards by
means of another SUT script:
from de.qfs.apps.qftest.extensions.checks import CheckerRegistry global allLabelsChecker def unregister(): try: CheckerRegistry.instance().unregisterChecker( "javax.swing.JPanel", allLabelsChecker) except: pass unregister()