I’m learning Python and have been trying to understand more about the details of Python’s unittest module. The documentation includes the following:
For the ease of running tests, as we
will see later, it is a good idea to
provide in each test module a callable
object that returns a pre-built test
suite:def suite(): suite = unittest.TestSuite() suite.addTest(WidgetTestCase('testDefaultSize')) suite.addTest(WidgetTestCase('testResize')) return suite
As far as I can tell, the purpose of doing this is not explained. In addition, I was unable to figure out how one would use such a method. I tried several things without success (aside from learning about the error messages I got):
import unittest
def average(values):
return sum(values) / len(values)
class MyTestCase(unittest.TestCase):
def testFoo(self):
self.assertEqual(average([10,100]),55)
def testBar(self):
self.assertEqual(average([11]),11)
def testBaz(self):
self.assertEqual(average([20,20]),20)
def suite():
suite = unittest.TestSuite()
suite.addTest(MyTestCase('testFoo'))
suite.addTest(MyTestCase('testBar'))
suite.addTest(MyTestCase('testBaz'))
return suite
if __name__ == '__main__':
# s = MyTestCase.suite()
# TypeError: unbound method suite() must be called
# with MyTestCase instance as first argument
# s = MyTestCase.suite(MyTestCase())
# ValueError: no such test method in <class '__main__.MyTestCase'>: runTest
# s = MyTestCase.suite(MyTestCase('testFoo'))
# TypeError: suite() takes no arguments (1 given)
The following “worked” but seems awkward and it required that I change the method signature of suite() to ‘def suite(self):‘.
s = MyTestCase('testFoo').suite()
unittest.TextTestRunner().run(s)
The very first error message you got is meaningful, and explains a lot.
Unbound. It means that you cannot call it unless you bind it to an instance. It’s actually the same for
MyTestCase.run:Maybe for now you don’t understand why you can’t call
suite, but please leave it aside for now. Would you try to callrunon the class, like above? Something like:Probably not, right? It does not make sense to write this, and it will not work, because
runis an instance method, and needs aselfinstance to work on. Well it appears that Python “understands”suitein the same way it understandsrun, as an unbound method.Let’s see why:
If you try to put the
suitemethod out of the class scope, and define it as a global function, it just works:But you don’t want it out of the class scope, because you want to call
MyTestCase.suite()You probably thought that since
suitewas sort of “static”, or instance-independent, it did not make sense to put aselfargument, did you?It’s right.
But if you define a method inside a Python class, Python will expect that method to have a
selfargument as a first argument. Just omitting theselfargument does not make your methodstaticautomatically. When you want to define a “static” method, you have to use the staticmethod decorator:This way Python does not consider MyTestCase as an instance method but as a function:
And of course now you can call
MyTestCase.suite(), and that will work as expected.