python裝飾器在接口自動化測試中的應用


在講解裝飾器在接口自動化測試項目的應用之前,我們先來介紹一下python裝飾器到底是個什么

裝飾器

說裝飾器就不得不提一下函數這個一等公民了,在python中函數有幾個特性先來了解一下

函數的一些特性
  • 函數也是對象

在python中函數也是對象,可以把函數賦值給變量,比如下面這樣:

def func(message):
    print("打印一條message: {}".format(message))
    
send_message = func
send_message("123")

我們把函數 func 賦予了變量 send_message,這樣之后你調用 send_message,就相當於是調用函數 func()

  • 把函數當做參數

函數也可以當做參數傳遞給另一個函數使用,比如:

def func(message):
    print("打印一條message: {}".format(message))

def call_func(func, message):
    func(message)
  • 函數的嵌套

函數的嵌套就是說在函數里再定義一個函數,比如這樣:

def call_func(message):
    def func(message):
        print("打印一條message: {}".format(message))
    return func(message)

上面在call_func的內部又定義了一個函數func,並在call_func里調用了這個內部的函數,調用后作為call_func的返回值返回

  • 函數的返回值也可以是函數對象

我們修改一下上面的例子。如下:

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'.

簡單的裝飾器
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() 不變.

語法糖@
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() 是需要接收參數,因為被裝飾函數是在裝飾器里執行,那就需要把函數接收的參數傳遞到裝飾器里,該怎么辦呢?很簡單,只需在裝飾器的嵌套函數上增加入參就行,比如

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

不過一般不這么一個個的寫,麻煩,直接這樣搞:

def my_decorator(func):
    def wrapper(*args, **kwargs):
        print('wrapper of decorator')
        func(*args, **kwargs)
    return wrapper
裝飾器也是可以接收參數的

裝飾器還有更大程度的靈活性,可以接受自己定義的參數,可以給裝飾器本身傳遞參數

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__()就會被執行一次。

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設置等功能。

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
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])

使用的時候要定義一個類,比如下面這樣:

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去請求接口。

以上實戰的具體代碼,當然這只是一部分,並不是完整的,后面爭取寫個系列文章,將這個接口自動化測試項目整體介紹一下,歡迎大家關注,多多交流!

歡迎大家去 我的博客 瞅瞅,里面有更多關於測試實戰的內容哦!!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM