簡體   English   中英

使用類裝飾器,如何在不重新定義類的情況下覆蓋方法?

[英]Using a class decorator, how to override a method without redefining the class?

對於使用App Engine測試平台的單元測試(使用unittest模塊),我需要使用setUptearDown方法分別激活和停用測試平台(略有簡化):

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()具體測試類super(SomeTestClass, self).setUp()無限遞歸結束,因為SomeTestClass現在等於WithTestbed

我對Python的運行時類型操縱有些困惑。 那么,如何正確地做到這一點呢?

這似乎可以解決問題:

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

有人看到這種方法有什么問題嗎?

這是使用子類而不是裝飾器來執行您要的操作的一種簡單方法:

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"

您要做的就是在測試用例中定義mySetUpmyTearDown而不是setUptearDown

這樣的事情會起作用:

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):
    ...

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM