Testing signals and slots
It's quite difficult to work the signals
and slots mechanism into the unittest framework. This is not
surprising, since signals and slots quintessentially join
components together, and the unittests are meant to test each
component separately.
However, you might want to test whether
calling a method on a certain object causes it to emit the right
signals. We need a bit of a custom framework for that purpose, a
kind of signal test.
You can use the
ConnectionBox from the following script
for that purpose. It is a simple class, derived from
QObject, which has one slot,
slotSlot(), that can be connected to a
signal with any number of arguments.
The arguments to the signal are stored in
the ConnectionBox, so they can be checked
later using the various assertion functions.
I have provided three assertion functions,
one to check whether the signal did arrive
(assertSignalArrived), one to check whether
the number of arguments was right,
(assertNumberOfArguments), and one to check
the types of the arguments using the Python
types
(assertArgumentTypes). This provides
typenames for all built-in types, but objects created from all
user-defined classes (including PyQt classes), belong to the
InstanceType. This means that you cannot
check whether you got a QListViewItem or
a QListView from a PyQt signal using this
function.
It would be a nice exercise to extend this
assert with checking objects using the
QObject.className()
method. Feel free...
#
# signals.py - unit-testing signals
#
import sys
import unittest
import types
from docviewdoc import DocviewDoc
from qt import *
class ConnectionBox(QObject):
def __init__(self, *args):
apply(QObject.__init__,(self,)+args)
self.signalArrived=0
self.args=[]
def slotSlot(self, *args):
self.signalArrived=1
self.args=args
def assertSignalArrived(self, signal=None):
if not self.signalArrived:
raise AssertionError, ("signal %s did not arrive" % signal)
def assertNumberOfArguments(self, number):
if number <> len(self.args):
raise AssertionError, \
("Signal generated %i arguments, but %i were expected" %
(len(self.args), number))
def assertArgumentTypes(self, *args):
if len(args) <> len(self.args):
raise AssertionError, \
("Signal generated %i arguments, but %i were given to this function" %
(len(self.args), len(args)))
for i in range(len(args)):
if type(self.args[i]) != args[i]:
raise AssertionError, \
( "Arguments don't match: %s received, should be %s." %
(type(self.args[i]), args[i]))
class SignalsTestCase(unittest.TestCase):
"""This testcase tests the testing of signals
"""
def setUp(self):
self.doc=DocviewDoc()
self.connectionBox=ConnectionBox()
def tearaDown(self):
self.doc.disConnect()
self.doc=None
self.connectionBox=None
def checkSignalDoesArrive(self):
"""Check whether the sigDocModified signal arrives"""
self.connectionBox.connect(self.doc, PYSIGNAL("sigDocModified"),
self.connectionBox.slotSlot)
self.doc.slotModify()
self.connectionBox.assertSignalArrived("sigDocModified")
def checkSignalDoesNotArrive(self):
"""Check whether the sigDocModifiedXXX signal does not arrive"""
self.connectionBox.connect(self.doc, PYSIGNAL("sigDocModifiedXXX"),
self.connectionBox.slotSlot)
self.doc.slotModify()
try:
self.connectionBox.assertSignalArrived("sigDocModifiedXXX")
except AssertionError:
pass
else:
fail("The signal _did_ arrive")
def checkArgumentToSignal(self):
"""Check whether the sigDocModified signal has the right
number of arguments
"""
self.connectionBox.connect(self.doc, PYSIGNAL("sigDocModified"),
self.connectionBox.slotSlot)
self.doc.slotModify()
self.connectionBox.assertNumberOfArguments(1)
def checkArgumentTypes(self):
"""Check whether the sigDocModified signal has the right
type of arguments.
"""
self.connectionBox.connect(self.doc, PYSIGNAL("sigDocModified"),
self.connectionBox.slotSlot)
self.doc.slotModify()
self.connectionBox.assertArgumentTypes(types.IntType)
def suite():
testSuite=unittest.makeSuite(SignalsTestCase, "check")
return testSuite
def main():
runner = unittest.TextTestRunner()
runner.run(suite())
if __name__=="__main__":
main()
Using this
ConnectionBox, you can test your
signals:
boud@calcifer:~/doc/pyqt/ch9 > python signals.py
Check whether the sigDocModified signal has the right number arguments ... ok
Check whether the sigDocModified signal has the right type of arguments ... ok
Check whether the sigDocModified signal arrives ... ok
Check whether the sigDocModifiedXXX signal does not arrive ... ok
------------------------------------------------------------------------------
Ran 4 tests in 0.003s
OK