[Python-Dev] Adding test case methods to TestCase subclasses

Ethan Furman ethan at stoneleaf.us
Fri Apr 15 21:40:26 CEST 2011


Ben Finney wrote:
> Ethan Furman <ethan at stoneleaf.us> writes:
> 
>> Here we have django's TestCase that does *not* want to call
>> unittest2.TestCase (assuming that's not a bug), but it gets called
>> anyway because the Mixin3 sibling has it as a base class.  So does
>> this mean that TestCase and Mixin3 just don't play well together?
>>
>> Maybe composition instead of inheritance is the answer (in this case,
>> anyway ;).
> 
> TestCase subclasses is a multiple-inheritance use case that I share. The
> mix-ins add test cases (methods named ‘test_’ on the mix-in class) to
> the TestCase subclass. I would prefer not to use multiple inheritance
> for this if it can be achieved in a better way.
> 
> How can composition add test cases detectable by Python's ‘unittest’?

Metaclasses, if's that an option...

8<-------------------------------------------------------------
import unittest
from composite import Composite  # python 3 only

class Spam():
     def test_spam_01(self):
         print('testing spam_01')
     def test_spam_02(self):
         print('testing spam_02')

class Eggs():
     def test_eggs_01(self):
         print('testing eggs_01')
     def test_eggs_02(self):
         print('testing eggs_02')

class TestAll(
         unittest.TestCase,
         metaclass=Composite,
         parts=(Spam, Eggs)):
     def setUp(self):
         print('Setting up...')
     def tearDown(self):
         print('Tearing down...')
     def test_something(self):
         print('testing something')

if __name__ == '__main__':
     unittest.main()
8<-------------------------------------------------------------

or a class decorator

8<-------------------------------------------------------------
class Compose(object):  # python 3 only
     def __init__(self, *parts):
         self.parts = parts
     def __call__(self, func):
         for part in self.parts:
             for attr in dir(part):
                 if attr[:2] == attr[-2:] == '__':
                     continue
                 setattr(func, attr, getattr(part, attr))
         return func

import unittest

class Spam():
     def test_spam_01(self):
         print('testing spam_01')
     def test_spam_02(self):
         print('testing spam_02')

class Eggs():
     def test_eggs_01(self):
         print('testing eggs_01')
     def test_eggs_02(self):
         print('testing eggs_02')

@Compose(Spam, Eggs)
class TestAll(unittest.TestCase):
     def setUp(self):
         print('Setting up...')
     def tearDown(self):
         print('Tearing down...')
     def test_something(self):
         print('testing something')

if __name__ == '__main__':
     unittest.main()
8<-------------------------------------------------------------

The decorator, as written, doesn't work on py2, and doesn't do any error 
checking (so overwrites methods in the final class) -- but I'm sure it 
could be spiffed up.

~Ethan~


More information about the Python-Dev mailing list