【妙用協程】 - 單元測試的setUp和tearDown

不少測試都須要在啓動的時候作一些事情,而後在結束的時候再把作的事情給清理了。通常的作法是把這些動做寫在setUp和tearDown的兩個方法裏,單元測試框架會負責在開始和結束的時候調用這兩個方法。app

class SomeTest(unittest.case.TestCase):
    def setUp(self):
        super(SomeTest, self).setUp()
        setup_db()

    def tearDown(self):
        clean_db()
        super(SomeTest, self).tearDown()

這種寫法有好幾個煩人的地方。首先是Logic Locality很差的問題:setup_db()和clean_db()是分在兩處的,中間可能隔着很長一段代碼。從視覺上沒法直觀的指導setup_db()原來和clean_db()是一對的。
其次是很難重用的問題(上綱上線的話就是複雜度很差管理的問題),爲了不重複寫公共的setUp和tearDown通常會抽取出一個UsingDbTest這樣的基類。這樣全部的子類必須記得super(xxx, self).setUp(),不然就會覆蓋掉基類的setUp。其次在須要有多個維度的東西須要複用的時候,好比有一個UsingDbTest的基類,有一個UsingNetworkTest的基類,難道讓子類繼承兩個基類麼(mixin是否是有點過於複雜了?)。
使用generator能夠很好的解決這個問題。首先咱們寫一個方法來作setUp和tearDown:框架

@contextlib.contextmanager
def using_db():
    setup_db()
    yield
    clean_db()

這樣能夠很是清晰地知道setup_db和clean_db是一對的。而後再把這個小的上下文附着到主測試邏輯上:單元測試

def apply_context(test, contextmanager):
    contextmanager.__enter__()
    test.addCleanup(lambda: contextmanager.__exit__(None, None, None))

class SomeTest(unittest.case.TestCase):
    def setUp(self):
        apply_context(self, using_db())

這裏利用了單元測試的addCleanup的特性,把tearDown轉化爲回調在setUpd的時候就設置好。利用這種方式,咱們能夠用組合的方式而不是繼承的方式來複用公共的setUp和tearDown的邏輯了。測試

相關文章
相關標籤/搜索