小言_互联网的博客

UI-自动化测试:ddt数据驱动与unittest用例关联

251人阅读  评论(0)

一、DDT数据驱动

1、DDT安装
windows、mac os系统,在CMD或者终端内输入:pip install ddt
2、DDT使用实例

import ddt
import unittest

@ddt.ddt
class TestDDT(unittest.TestCase):
    def setUp(self):
        pass
    def tearDown(self):
        pass
    @ddt.data(1,2,3)
    def test_01(self, a):
        self.assertEqual(a,2)

运行结果如图:

3、DDT中data所可以使用的数据类型
单参数可以传int, str

@ddt.data('1','2','3')
    def test_01(self, a):
        self.assertEqual(a,2)

运行结果如图:

多参数可以传tuple, list, dict

@ddt.data([1,2], [2, 3])
    @ddt.unpack
    def test_01(self, a, b):
        self.assertEqual(a,b)
@ddt.data({
   'a':'1', 'b':'2'}, {
   'a':'1', 'b':'2'}, {
   'a':'1', 'b':'2'})
    @ddt.unpack
    def test_02(self, a, b):
        self.assertEqual(b, a)

运行结果如图:

我们通过观察上面单参数和多参数,可以总结出一些特点和步骤:
步骤:
第一步:需要使用ddt的文件,先要导入ddt包,import ddt
第二步,在测试类上面,使用ddt装饰器,
第三步,在需要大量数据进行验证的测试方法上面使用@ddt.data()
注意:ddt.data()这个方法是紧挨着要使用数据的测试用例的。
特点:
(1)DDT传递数据类型,与测试用例中有几个参数密切相关
(2)测试用例特点,单参数、列表、元组用例类型类似,字典参数需要单独记录,如:
单参数:方法名_编号_参数1
多参数:方法名_编号_参数1_参数2_… …
字典类型参数: 方法名_编号
(3)unpack使用时机:当参数为多个的时候,需要使用@ddt.unpack, 可以理解为将数据包拆分
4、如何批量传递数据,并自动生成希望的变量格式
以excel批量传递测试数据为例:
(1)读取excel方法

# 引入一个新包,pip install pandas
import pandas as pd
class readExcelMethod():
    """
    :fileName: 文件路径
    :sheetName: sheet页的名称
    """

    def __init__(self, fileName: str, sheetName='Sheet1'):
        self.fileName = fileName  # 文件路径
        self.sheetName = sheetName  # excel sheet页的名称
        super(readExcelMethod, self).__init__()

    def readExcel(cls):
        df = pd.read_excel(cls.fileName, sheet_name=cls.sheetName)
        df = df.T
        testData = []
        for i in range(len(df)):
            testData.append(list(df[i]))
        return testData

excel表格内容如图:

代码运行结果如图:

(2)如何将数据放入ddt.data()中,非常简单,代码如图:

data = readExcelMethod(fileName='').readExcel()
@ddt.data(*data)

仅需要在读取出的excel数据前增加一个【*】,便可以了。

二、unittest用例关联

为什么单独讲一下unittest的用例关联,是因为unittest没有现成的用例关联模块可以进行调用,另外,用例与用例之间往往存在较强关联性,举例:登录与退出的用例,退出用例的前提便是需要先要登录成功才会验证退出,结果登录已经失败了,其实很多情况便没有必要在去执行退出的操作了。
因此需要单独设计用例依赖的方法
其实用例设计依赖的核心思想,在于将依赖用例的执行状态存起来
因此这个存,便非常有讲究了
1、存在哪里?怎么存?
(1)借助辅助工具,如数据库,excel,redis等媒介进行存储
(2)存在本地缓存,python 有现成的缓存方法,在functools包下,如:lru_cache, 引入方法:from functools import lru_cache
(3)存在迭代器中,如List, dict中
知道了往哪里存, 怎么存了,但是存什么那?
2、存什么?怎么处理?
精细存:存具体用例失败的原因,比如:异常原因,或者失败的原因,根据具体异常,去存储对应的信息
粗略存:捕捉失败原因,正确存True,错误存False, 根据异常存储True or False
3、如何管理用例
首先跳过测试的方法需要用到unittest.TestCase().SkipTest(),该方法为unittest框架中,内部跳过测试用例的方法
(1)第一种:如果A与D是有关联的,这时候,执行A,A通过与否都去执行D用例,不需要等到D用例执行,跳过B,C用例,直接执行D用例
(2)第二种:如果A与D是有关联的,这时候,执行A用例,只需要将A用例的测试结果存在临时地方,然后待D执行的时候,在去获取A的结果,来判断D是否执行
1)通过辅助工具
2)通过缓存
3)利用迭代器
4、本博主原创设计的2种用例关联的方法:
(1)利用迭代器的做法,效果可以自己体验

from functools import wraps
import unittest

testCaseName = {
   }
class decRelation:
    """
    :name:被依赖的用例名称
    :depend:依赖的用例名称
    =======example=======
    @decRelation(name='test_01')
    def test_01(self):
        self.assertEqual(2, 2)

    @decRelation(depend='test_01')
    def test_02(self):
        self.assertEqual(1, 2)
    """
    def __init__(self, name=None, depend=None):
        self.depend = depend
        self.name = name
        super(decRelation, self).__init__()

    def __call__(self, func):
        @wraps(func)
        def decoration(*args, **kwargs):
            try:
                if self.name:
                    if self.depend and testCaseName[self.depend] is True:
                        func(*args, **kwargs)
                        testCaseName[self.name] = True
                    elif self.depend is None:
                        func(*args, **kwargs)
                        testCaseName[self.name] = True
                if self.depend and testCaseName[self.depend] is not True:
                    unittest.TestCase().skipTest(reason="%s 测试用例执行失败,相关依赖用例跳过!!!" % (self.depend))
                    testCaseName[func.__name__] = 'Skip'
                else:
                    result = func(*args, **kwargs)
                    testCaseName[func.__name__] = True
                    return result
            except Exception as e:
                if self.name and self.name not in testCaseName.keys():
                    testCaseName[self.name] = False
                if self.depend and self.depend not in testCaseName.keys():
                    testCaseName[self.depend] = False
                raise e
        return decoration

(2)利用excel做中间器的方式,适合excel存放大批量测试用例的方式
读取excel与保存excel方法封装

class dealExcelMethod():
    """
    :fileName: 文件路径
    :sheetName: sheet页的名称
    """
    def __init__(self, fileName:str, sheetName='Sheet1'):
        self.fileName = fileName  # 文件路径
        self.sheetName = sheetName  # excel sheet页的名称
        super(dealExcelMethod, self).__init__()

    # 读取数据
    def readExcelDDt(cls):
        df = pd.read_excel(cls.fileName, sheet_name=cls.sheetName, keep_default_na=False)
        listNum = len(df)
        df = df.T
        testData = []
        for i in range(listNum):
            testData.append(list(df[i]))
        return testData

    # 写入数据
    def saveExcel(cls, testMethod:str, statusCode:str, testCaseNum:int):
        df = pd.read_excel(cls.fileName, sheet_name=cls.sheetName, keep_default_na=False)
        df.loc[testCaseNum, 'testName'] = testMethod
        df.loc[testCaseNum, 'result'] = statusCode
        writer = pd.ExcelWriter(cls.fileName)
        df.to_excel(excel_writer=writer, index=False)
        writer.save()
        return df


这里的字段,需要根据自己实际所需要的字段进行填写,我只是这个作为例子,进行演示一下excel设计的方法
实际测试用例使用部分代码

from inspect import stack

# 通过辅助工具做用例关联
# example: excel

from webUI.readExcel import dealExcelMethod
import unittest

def readExcel():
    fileName = ''
    data = dealExcelMethod(fileName=fileName, sheetName='Sheet1').readExcelDDt()
    return data

def saveExcel(testMethod,statusCode,testCaseNum):
    fileName = ''
    dealExcelMethod(fileName=fileName, sheetName='Sheet1').saveExcel(testMethod,statusCode,testCaseNum)


class TestExcel(unittest.TestCase):

    def test_01(self):
        try:
            self.assertEqual(1,2)
            saveExcel(testMethod=stack()[0][3], statusCode='True', testCaseNum=0)
        except Exception as e:
            saveExcel(testMethod=stack()[0][3], statusCode='False', testCaseNum=0)
            raise e

    def test_02(self):
        try:
            print(readExcel())
            if readExcel()[1][1]:
                self.assertEqual(2,2)
            else:
                self.skipTest(reason='该条测试用例跳过')
        except Exception as e:
            raise e


excel的方法,仅作为抛转引玉,为大家提供一个思路。
其实第一种方法,那个方法在很多情况,可以解决当前所需要的用例关联问题,并且代码简洁清晰,若是大家需要做用例关联的话,建议用第一种方法。另外,希望大家觉得我的方法还可以,希望大家能给点个赞,加个关注,后续我有好的方法,我会继续分享的~


转载:https://blog.csdn.net/weixin_46198734/article/details/109805772
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场