Анализ исходного кода декораторов @ddt и @data в автоматизированном тестировании Python

вставьте сюда описание изображения

1. Общая схема использования ddt и декораторов данных следующая: каждый метод, начинающийся с test_, представляет собой тестовый пример.

from ddt import ddt,data
import unittest


test_datas=[
    {
    
    'id':1,'title':'测试用例1'},
    {
    
    'id':2,'title':'测试用例2'},
    {
    
    'id':3,'title':'测试用例3'},
    {
    
    'id':4,'title':'测试用例4'}
]

@ddt
class TestDemo(unittest.TestCase):

    @data(*test_datas)
    def test_demo1(self,item):
        print('测试用例执行',item)

Тестовый случай в юниттесте:

Метод в начале каждого теста в тестовом классе — это тестовый пример.

Идея ​ddt, генерирующего тестовые сценарии на основе данных вариантов использования:

1. Используйте декоратор данных: передайте тестовые данные и сохраните тестовые данные в декораторе
2. Декоратор ddt: просмотрите тестовые данные и добавьте метод setattr
(класс, имя метода, метод)

2. Динамически добавлять методы к классам

Дело 1

setattr(объект/класс, имя атрибута/имя метода, значение атрибута/метод)

обращать внимание:

Чтобы динамически добавлять методы в класс, вы должны добавить self

class Demo:

    def test_1(self):
        print("这个是方法test_1")


def kobe(self,item):
    print("kobe-----执行了",item)

datas=[2,8,23,22,24]

#根据数据动态给测试类中增加5个方法
for i in datas:
    name='test_1_{}'.format(i)
    #给类动态增加方法
    setattr(Demo,name,kobe)

print(Demo.__dict__)

вставьте сюда описание изображения

Случай 2: Вызвать 5 методов для динамического выполнения, а результаты выполнения все кобе-----24 выполнения, есть баг

class Demo:

    def test_1(self):
        print("这个是方法test_1")


def kobe(self,item):
    print("kobe-----执行了",item)

datas=[2,8,23,22,24]

#根据数据动态给测试类中增加5个方法
for i in datas:
    name='test_1_{}'.format(i)

    def wrapper(self):
        kobe(self,i)

    #给类动态增加方法
    setattr(Demo,name,wrapper)

#print(Demo.__dict__)

Demo().test_1_2()
Demo().test_1_8()
Demo().test_1_22()
Demo().test_1_23()
Demo().test_1_24()

Результат выполнения:
kobe ----- выполнено 24
kobe ----- выполнено 24
kobe ----- выполнено 24
kobe ----- выполнено 24
kobe ----- выполнено 24

Анализ причин

вставьте сюда описание изображения

Случай 3: Решить ошибку случая 2

Определите замыкание create_method: для блокировки данных заблокированные данные=[2,8,23,22,24]

class Demo:

    def test_1(self):
        print("这个是方法test_1")


def kobe(self,item):
    print("kobe-----执行了",item)

datas=[2,8,23,22,24]

#todo 使用闭包进行数据锁定
def create_method(i):
    def wrapper(self):
        kobe(self,i)
    return wrapper


#根据数据动态给测试类中增加5个方法
for i in datas:
    name='test_1_{}'.format(i)

    wrapper=create_method(i)

    #给类动态增加方法
    setattr(Demo,name,wrapper)

Demo().test_1_2()
Demo().test_1_8()
Demo().test_1_22()
Demo().test_1_23()
Demo().test_1_24()

3. Анализ исходного кода ddt и данных

from ddt import ddt,data
import unittest


test_datas=[
    {
    
    'id':1,'title':'测试用例1'},
    {
    
    'id':2,'title':'测试用例2'},
    {
    
    'id':3,'title':'测试用例3'},
    {
    
    'id':4,'title':'测试用例4'}
]


def ddt(cls):
    '''遍历测试数据,给类动态添加方法'''
    #如何通过类获取方法?
    #res=cls.__dict__
    #print('测试类的方法和属性字典',res)
    for name,method in list(cls.__dict__.items()):
        #遍历出来的属性值(方法)是否拥有datas属性(测试数据)
        if hasattr(method,'datas'):
            #获取方法中保存的测试数据
            datas=getattr(method,'datas')
            #遍历测试数据
            for index,value in enumerate(datas):
                print("数据:",value)
                #给测试类动态添加用例
                method_name='{}_{}'.format(name,index+1)
                print('方法名',method_name)
				
				#给类动态的增加方法
                def wrapper(self):
                    method(self, value)

                #todo 给测试类动态添加一个测试方法
                setattr(cls,method_name,wrapper)


    return cls

def data(*args):
    '''将测试数据保存为测试方法的属性'''
    #*args接收到的是data装饰器传递进来的数据
    def wrapper(func):
        #func接收的是data装饰的函数
        func.datas=args
        return func

    return wrapper


@ddt
class TestDemo():

    @data(*test_datas)      #test_demo1=data(*test_datas)(test_demo1)
    def test_demo1(self,item):
        print('测试用例执行',item)

#print(TestDemo.test_demo1.__dict__)

Есть ошибка, по которой нужно писать так
вставьте сюда описание изображения
:
вставьте сюда описание изображения

решать:

Используйте замыкания для блокировки данных, значения блокировки и метода

def create_test_method(method,value):
    def wrapper(self):
        method(self, value)
    return wrapper

from ddt import ddt,data
import unittest


test_datas=[
    {
    
    'id':1,'title':'测试用例1'},
    {
    
    'id':2,'title':'测试用例2'},
    {
    
    'id':3,'title':'测试用例3'},
    {
    
    'id':4,'title':'测试用例4'}
]

def create_test_method(method,value):
    def wrapper(self):
        method(self, value)
    return wrapper

def ddt(cls):
    #todo @ddt这个装饰器:遍历测试数据,每遍历出一条数据,往测试类中添加一个test开头的方法
    #setattr(类,方法名,方法)
    res=list(cls.__dict__.items())
    print(res)
    for name,method in res:
        print(name,method)
        if hasattr(method,'datas'):
            #如果有datas属性,获取方法中保存的datas
            datas=getattr(method,'datas')
            #遍历测试数据
            for index,value in enumerate(datas):
                print('测试数据:',value)

                #给测试类动态的增加测试用例
                method_name='{}_{}'.format(name,index+1)
                print('方法:',method_name,method)

                #todo 给类动态的增加方法,最终希望执行def test_demo1(self,item):这个方法的
                #test_method=method
                #但是item需要自己传,但是unittest是不需要传递参数的
                
                # def wrapper(self):
                #     method(self,value)

                wrapper=create_test_method(method, value)

                # todo 给测试类动态添加一个测试方法
                setattr(cls, method_name, wrapper)
            else:
                delattr(cls,name)
    return cls

def data(*args):
    # *args为给装饰器传递的参数test_datas

    def wrapper(func):
        # func为被装饰器装饰的函数test_demo1
        #todo @data装饰器的作用是保存测试数据,将测试数据存放到函数属性中
        func.datas = test_datas
        return func

    return wrapper


@ddt
class TestDemo(unittest.TestCase):

    @data(*test_datas)      #test_demo1=data(*test_datas)(test_demo1)
    def test_demo1(self,item):
        print('测试用例执行',item)

Код частичного разбора

@data(*test_datas)    
def test_demo1(self,item):
    print('测试用例执行',item)

1. Приведенные выше 3 строки кода можно записать следующим образом:

@data(*test_datas): Может быть выражено как test_demo1=data( *test_datas)(test_demo1)

2. Имя выходного атрибута (метода) и значение атрибута

for name,method in list(cls.__dict__.items())

вставьте сюда описание изображения

3. Определить, содержит ли имя пройденного атрибута (метод) атрибут datas.Если атрибут datas есть, получить данные, сохраненные в методе

if hasattr(method,'datas'):
	datas=getattr(method,'datas')

вставьте сюда описание изображения

Guess you like

Origin blog.csdn.net/YZL40514131/article/details/126679826
Recommended