对于使用App Engine testbed的单元测试(使用unittest
模块),我需要setUp
和tearDown
方法分别激活和停用测试平台(略微简化) ):
class SomeTest(unittest.TestCase):
def setUp(self):
self.testbed = testbed.Testbed()
self.testbed.activate()
def tearDown(self):
self.testbed.deactivate()
def testSomething(self):
...
这很快成为写作的负担。我可以写一个基类TestCaseWithTestbed
,但是每次我需要在其中一个测试用例中使用自定义setUp
时,我必须记得调用超类方法。
我认为用类装饰器解决这个问题会更优雅。所以我想写一下:
@WithTestbed
class SomeTest(unittest.TestCase):
def testSomething(self):
...
应用这个装饰器后,应该神奇地激活测试台。那么......如何实现WithTestbed
装饰器?我目前有以下内容:
def WithTestbed(cls):
class ClsWithTestbed(cls):
def setUp(self):
self.testbed = testbed.Testbed()
self.testbed.activate()
cls.setUp(self)
def tearDown(self):
cls.tearDown(self)
self.testbed.deactivate()
return ClsWithTestbed
这适用于简单的情况,但有一些严重的问题:
ClsWithTestbed
,并显示在测试输出中。super(SomeTestClass, self).setUp()
的具体测试类最终会进行无限递归,因为SomeTestClass
现在等于WithTestbed
。我对Python的运行时类型操作有点模糊。那么,如何以正确的方式做到这一点?
答案 0 :(得分:2)
这似乎可以解决问题:
def WithTestbed(cls):
def DoNothing(self):
pass
orig_setUp = getattr(cls, 'setUp', DoNothing)
orig_tearDown = getattr(cls, 'tearDown', DoNothing)
def setUp(self):
self.testbed = testbed.Testbed()
self.testbed.activate()
orig_setUp(self)
def tearDown(self):
orig_tearDown(self)
self.testbed.deactivate()
cls.setUp = setUp
cls.tearDown = tearDown
return cls
有没有人发现这种方法存在任何问题?
答案 1 :(得分:1)
这是一个简单的方法,可以用子类而不是装饰器来做你想要的事情:
class TestCaseWithTestBed(unittest.TestCase):
def setUp(self):
self.testbed = testbed.Testbed()
self.testbed.activate()
self.mySetUp()
def tearDown(self):
self.myTearDown()
self.testbed.deactivate()
def mySetUp(self): pass
def myTearDown(self): pass
class SomeTest(TestCaseWithTestBed):
def mySetUp(self):
"Insert custom setup here"
您只需在测试用例中定义mySetUp
和myTearDown
,而不是setUp
和tearDown
。
答案 2 :(得分:1)
这样的事情会起作用:
def WithTestbed(cls):
cls._post_testbed_setUp = getattr(cls, 'setUp', lambda self : None)
cls._post_testbed_tearDown = getattr(cls, 'tearDown', lambda self : None)
def setUp(self):
self.testbed = testbed.Testbed()
self.testbed.activate()
self._post_testbed_setUp()
def tearDown(self):
self.testbed.deactivate()
self._post_testbed_tearDown()
cls.setUp = setUp
cls.tearDown = tearDown
return cls
@WithTestbed
class SomeTest(object):
...