1. 程式人生 > >python裝飾器在介面自動化測試中的應用

python裝飾器在介面自動化測試中的應用

在講解裝飾器在介面自動化測試專案的應用之前,我們先來介紹一下python裝飾器到底是個什麼 ### 裝飾器 說裝飾器就不得不提一下函式這個一等公民了,在python中函式有幾個特性先來了解一下 ##### 函式的一些特性 * 函式也是物件 在python中函式也是物件,可以把函式賦值給變數,比如下面這樣: ```python def func(message): print("列印一條message: {}".format(message)) send_message = func send_message("123") ``` 我們把函式 func 賦予了變數 send_message,這樣之後你呼叫 send_message,就相當於是呼叫函式 func() * 把函式當做引數 函式也可以當做引數傳遞給另一個函式使用,比如: ```python def func(message): print("列印一條message: {}".format(message)) def call_func(func, message): func(message) ``` * 函式的巢狀 函式的巢狀就是說在函式裡再定義一個函式,比如這樣: ```python def call_func(message): def func(message): print("列印一條message: {}".format(message)) return func(message) ``` 上面在call_func的內部又定義了一個函式func,並在call_func裡呼叫了這個內部的函式,呼叫後作為call_func的返回值返回 * 函式的返回值也可以是函式物件 我們修改一下上面的例子。如下: ```python def call_func(): def func(message): print("列印一條message: {}".format(message)) return func result = call_func() result("hello world") ``` 函式 call_func() 的返回值是函式物件 func 本身,之後,我們將其賦予變數 result,再呼叫 result(‘hello world’),最後輸出了'列印一條message: hello world'. ##### 簡單的裝飾器 ```python def my_decorator(func): def wrapper(): print('wrapper of decorator') func() return wrapper def greet(): print('hello world') greet = my_decorator(greet) greet() # 輸出 wrapper of decorator hello world ``` 變數 greet 指向了內部函式 wrapper(),而內部函式 wrapper() 中又會呼叫原函式 greet(),因此,最後呼叫 greet() 時,就會先列印'wrapper of decorator',然後輸出'hello world'。這裡的函式 my_decorator() 就是一個裝飾器,它把真正需要執行的函式 greet() 包裹在其中,並且改變了它的行為,但是原函式 greet() 不變. ##### 語法糖@ ```python def my_decorator(func): def wrapper(): print('wrapper of decorator') func() return wrapper @my_decorator def greet(): print('hello world') greet() ``` 這裡的@,我們稱之為語法糖,@my_decorator就相當於前面的greet=my_decorator(greet)語句,只不過更加簡潔。因此,如果你的程式中有其它函式需要做類似的裝飾,你只需在它們的上方加上@decorator就可以了,這樣就大大提高了函式的重複利用和程式的可讀性。 ##### 帶引數的裝飾器 如果原函式 greet() 是需要接收引數,因為被裝飾函式是在裝飾器裡執行,那就需要把函式接收的引數傳遞到裝飾器裡,該怎麼辦呢?很簡單,只需在裝飾器的巢狀函式上增加入參就行,比如 ```python def my_decorator(func): def wrapper(message): print('wrapper of decorator') func(message) return wrapper @my_decorator def greet(message): print(message) greet('hello world') # 輸出 wrapper of decorator hello world ``` 不過一般不這麼一個個的寫,麻煩,直接這樣搞: ```python def my_decorator(func): def wrapper(*args, **kwargs): print('wrapper of decorator') func(*args, **kwargs) return wrapper ``` ##### 裝飾器也是可以接收引數的 裝飾器還有更大程度的靈活性,可以接受自己定義的引數,可以給裝飾器本身傳遞引數 ```python def repeat(num): def my_decorator(func): def wrapper(*args, **kwargs): for i in range(num): print('wrapper of decorator') func(*args, **kwargs) return wrapper return my_decorator @repeat(4) def greet(message): print(message) greet('hello world') ``` ##### 類裝飾器 類也可以作為裝飾器。類裝飾器主要依賴於函式__call__(),每當你呼叫一個類的示例時,函式__call__()就會被執行一次。 ```python class Request: def __init__(self, func): self.func = func self.num_calls = 0 def __call__(self, *args, **kwargs): self.num_calls += 1 print('num of calls is: {}'.format(self.num_calls)) return self.func(*args, **kwargs) @Request def example(): print("hello world") example() # 輸出 num of calls is: 1 hello world example() # 輸出 num of calls is: 2 hello world ... ``` 這個類裝飾器還不支援接收引數,後面我們實戰的裝飾器時可以支援結束引數的。 ### 裝飾器在介面自動化測試專案中應用 至此我們介紹完了裝飾器,下面我們基於之前的理論,來進行一次實戰。 需求是希望通過裝飾器來實現介面的請求,能夠自定義請求方法、請求的根路徑、公共引數、headers設定等功能。 ```python class Request: def __init__(self, url='', method='get'): '''''' self.url = url # 請求路徑 self.method = method # 請求方法 self.func_return = None # 被裝飾器標記的方法的返回引數 self.func_im_self = None # 被裝飾器標記的方法的類的例項 self.session = None # 當前使用的會話物件 def __call__(self, func): self.func = func self.is_class = False try: if inspect.getfullargspec(self.func).args[0] == 'self': self.is_class = True except IndexError: pass def fun_wrapper(*args, **kwargs): # 呼叫被裝飾標記的方法,這個方法會返回請求介面所需要的返回值 self.func_return = self.func(*args, **kwargs) or {} self.func_im_self = args[0] if self.is_class else object self.create_url() self.create_session() self.session.headers.update(getattr(self.func_im_self, 'headers', {})) self.decorator_args.update(getattr(self.func_im_self, 'common_params', {})) self.decorator_args.update(self.func_return) return Request(self.method, self.url, self.session) return fun_wrapper ``` ```python def create_url(self): """ 生成http請求的url,跟路徑和介面路由進行拼接 """ base_url = getattr(self.func_im_self, 'base_url', '') self.url = self.func_return.pop('url', None) or self.url self.url = ''.join([base_url, self.url]) ``` 使用的時候要定義一個類,比如下面這樣: ```python class AdvertService: def __init__(self): self.common_params = {} # 定義介面請求的公共引數 self.headers = {} # 定義請求的header self.base_url = self._config.AD_ADMIN_ROOT_URL @Request(url=“/v3/advert/create”, method='post') def _create_ad(self, advert: Advert): return dict(json=advert) ``` 上面的header會被自動的新增的session的header裡,common_params也會被新增到引數裡,base_url和裝飾器裡傳的url會被拼接成一個完整的url去請求介面。 以上實戰的具體程式碼,當然這只是一部分,並不是完整的,後面爭取寫個系列文章,將這個介面自動化測試專案整體介紹一下,歡迎大家關注,多多交流! > 歡迎大家去 [我的部落格](https://www.immortalp.com) 瞅瞅,裡面有更多關於測試實戰的內容