python使用裝飾器在執行單元測試時配置環境

裝飾器的定義是給一個對象動態載入功能,就像打遊戲時給隊友上buff一樣。一直以來,我對裝飾器用的不多,經常會用別的方式搞定,雖然代碼丑一點,但也能用。這次遇到一個特別適合裝飾器的應用場景,就是執行單元測試時的環境配置。

我是用pytest做單元測試,測試入口都是一個個test_打頭的函數。和unittest不一樣,pytest中並沒有setUp這個方法,雖然有fixture,但讀人家的源碼時也很少看到有人用,這次遇到問題發現,我靠,就是加個裝飾器的事,可以把setUp和tearDown一起做了,何必多此一舉。

應用場景用個demo舉例,由於生產環境和測試環境的不同,在測試環境中初始化Demo會報錯,比如下面這個模塊。

import sysnnclass Demo():n def __init__(self):n fail()nndef fail():n if sys.argv[0].split()[-1].find(test) > -1:n raise EnvironmentError(__name__)nndef success():n print(__name__)n

定義了一個Demo類,初始化時會調用fail函數,這個函數在pytest環境下使用時會raise一個EnvironmentError。解決方案就是在這個Demo被調用時將模塊中的fail函數替換為success函數。兩個單元測試用例如下。

import demondef test_demo():n try:n demo.Demo()n except Exception as e:n assert isinstance(e, EnvironmentError)nn@replace#替換環境的裝飾器ndef test_replace_demo():n demo.Demo()n

其中,replace就是替換環境用的裝飾器。裝飾器代碼如下。

def replace(fun):#定義裝飾器,傳入函數名n _ = demo.fail#保存模塊中的fail,以便後面恢復n demo.fail = demo.success#更改n def inner():#閉包函數n fun()n demo.fail = _#恢復模塊中fail函數n return inner()n

用裝飾器配置單元測試環境真是優雅無比,寫完後頓時腰不酸背不痛了呢。。。

裝飾器的難點之一就是閉包(closure),閉包這個翻譯在中文裡沒有很形象的對應關係,這造成了理解障礙。這種例子挺多的,就像單例模式最廣泛的用途並不是提供一個的「單例」,叫「超級全局宇宙唯一變數」比較好。協程的功能里一點『協』的作用都沒有,叫「想在什麼時候掛起就在什麼適合掛起程」比較好。這樣一想,閉包是不是叫「跨作用域包」或「腳踏兩條船包」或「閉合環境打包」比較好。

閉包的一個定義是這樣的:

Closures are functions that refer to independent (free) variables (variables that are used locally, but defined in an enclosing scope). In other words, these functions remember the environment in which they were created.n

閉包的關鍵能力之一是獲取上級作用域,另一個關鍵點在於python中函數是一個對象,可以傳來傳去,比如作為返回值。這兩點結合起來就可以將函數所處的環境打包傳到目標區域。在上面一個例子中inner就是一個閉包函數,它在這裡的作用就是將inner函數之前的那個區域,就是第二行和第三行,這一環境打包,具體如圖所示。

下面還有個閉包的例子,可以看到在inner外定義了a,b,在inner中使用了a,在test_closure函數實例化後可以看看其__closure__方法中的對象,這個__closure__只包含了a,並沒包含b,因為b沒有在inner中使用,被垃圾回收了,而a保留了下來,而a就是由於閉包的特性保留下來的,可以用pdb來看看__closure__中是否保留了b。

def test_closure():n a,b = 1,2n def inner():n print(a)n return innernclose = test_closure()nprint(close.__closure__[0])#<cell at 0x01CCC4B0: int object at 0x5B975910>n

另外,python中的閉包和javascript中的閉包是一個意思,可能需要實現的功能沒js中那麼多,但理解python閉包時可以參考js的教程。

推薦閱讀:

為何前端面試官都喜歡問閉包?
腳本語言如何實現閉包?
Python中後期綁定(late binding)是什麼意思?
如何通俗地解釋閉包的概念?
iOS
閉包中的[weak self]在什麼情況下需要使用,什麼情況下可以不加?

TAG:闭包 | 单元测试 | 装饰器 |