Application of python decorator in interface automation testing

Table of contents

Foreword:

decorator

Some properties of functions

simple decorator

syntactic sugar @

Decorator with parameters

Decorators can also receive parameters

class decorator

The decorator is applied in the interface automation test project


Foreword:

Python decorators are widely used in interface automation testing. A decorator is a special Python syntax that can be used to modify or extend the behavior of a function without modifying the function's definition. In interface automation testing, decorators can be used to add additional functions, such as logging, exception handling, performance statistics, etc.

Before explaining the application of decorators in interface automation testing projects, let's first introduce what python decorators are

decorator

Speaking of decorators, I have to mention functions as first-class citizens. In python, functions have several characteristics. Let’s first understand them.

Some properties of functions
  • functions are objects

In python, functions are also objects, and functions can be assigned to variables, such as the following:

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

send_message = func
send_message("123")

We assigned the function func to the variable send_message, so that after you call send_message, it is equivalent to calling the function func()

  • function as parameter

Functions can also be passed as parameters to another function, for example:

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

def call_func(func, message):
    func(message)
  • Nesting of functions

The nesting of functions means to define another function inside a function, such as this:

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

The above defines a function func inside call_func, and calls this internal function in call_func, and returns it as the return value of call_func after calling

  • The return value of a function can also be a function object

Let's modify the example above. as follows:

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

result = call_func()
result("hello world")

The return value of the function call_func() is the function object func itself. After that, we assign it to the variable result, then call result('hello world'), and finally output 'print a message: hello world'.

simple decorator
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

The variable greet points to the internal function wrapper(), and the internal function wrapper() calls the original function greet(). Therefore, when calling greet() at the end, it will print 'wrapper of decorator' first, and then output 'hello world '. The function my_decorator() here is a decorator, which wraps the function greet() that actually needs to be executed, and changes its behavior, but the original function greet() remains unchanged.

syntactic sugar @
def my_decorator(func):
    def wrapper():
        print('wrapper of decorator')
        func()
    return wrapper

@my_decorator
def greet():
    print('hello world')

greet()

The @ here is called syntactic sugar, and @my_decorator is equivalent to the previous greet=my_decorator(greet) statement, but it is more concise. Therefore, if there are other functions in your program that need to be decorated similarly, you only need to add @decorator above them, which greatly improves the reuse of functions and the readability of the program.

Decorator with parameters

If the original function greet() needs to receive parameters, because the decorated function is executed in the decorator, then the parameters received by the function need to be passed to the decorator, what should we do? It's very simple, just add parameters to the nested function of the decorator, such as

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

However, it is generally not written one by one, troublesome, just do it like this:

def my_decorator(func):
    def wrapper(*args, **kwargs):
        print('wrapper of decorator')
        func(*args, **kwargs)
    return wrapper
Decorators can also receive parameters

The decorator also has a greater degree of flexibility, can accept parameters defined by itself, and can pass parameters to the decorator itself

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

class decorator

Classes can also act as decorators. Class decorators mainly rely on the function call ( ), which is executed once every time you call an instance of a class .

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

...

This class decorator does not support receiving parameters yet, and we can support end parameters in the actual decorator later.

The decorator is applied in the interface automation test project

So far we have introduced the decorator, now we will conduct an actual combat based on the previous theory.

The requirement is to implement the request of the interface through the decorator, and to be able to customize the request method, the root path of the request, public parameters, headers settings and other functions.

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

When using it, you need to define a class, such as the following:

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)

The above header will be automatically added to the session header, common_params will also be added to the parameters, base_url and the url passed in the decorator will be spliced ​​into a complete url to request the interface.

  As someone who has been here, I also hope that everyone will avoid some detours

Here I will share with you some necessities of the way forward in automated testing, hoping to help you.

(WEB automated testing, app automated testing, interface automated testing, continuous integration, automated test development, big factory interview questions, resume templates, etc.)

I believe it can make you better progress!

Click on the small card below

Guess you like

Origin blog.csdn.net/Free355/article/details/131701164