• 0
  • 0
分享
  • 数据驱动测试从方法探研到最佳实践——软件测试圈
  • quinn 2022-12-14 11:33:09 字数 5716 阅读 591 收藏 0

导读

在自动化测试实践中,测试数据是制造测试场景的必要条件,本文主要讲述了在沟通自动化框架如何分层,数据如何存储,以及基于单元测试 pytest 下如何执行。并通过实践案例分享,提供数据驱动测试的具体落地方案。

基本概念

数据驱动测试(DDT)是一种方法,其中在数据源的帮助下重复执行相同顺序的测试步骤,以便在验证步骤进行时驱动那些步骤的输入值和 / 或期望值。在数据驱动测试的情况下,环境设置和控制不是硬编码的。换句话说,数据驱动的测试是在框架中构建要与所有相关数据集一起执行的测试脚本,该脚本利用了可重用的测试逻辑。数据驱动的测试提供了可重复性,将测试逻辑与测试数据分离以及减少测试用例数量等优势。

设计思路

2.1 测试数据

在测试过程中往往需要更加充分地测试场景,而创建数据测试。测试数据包括输入输出,对输出的自动化验证等。创建测试数据,可以通过手动拼装,生产环境拷贝,或通过自动化工具生成。

2.2 数据存储

数据驱动测试中使用的数据源可以是 Excel 文件,CSV 文件,Yaml 文件,数据池,ADO 对象或 ODBC 源。

2.3 数据驱动优势

1. 如果应用程序开发还在进行当中,测试者仍然可以进行脚本的编写工作。

2. 减少了冗余和不必要的测试脚本。

3. 用较少的代码生成测试脚本。

4. 所有信息,如输入、输出和预期结果,都以适当的文本记录形式进行存储。

5. 为应用程序的维护提供利了灵活性条件。

6. 如果功能发生了变化,只需要调整特定的函数脚本。

实践分享

基于 Laputa 框架现有测试脚本,抽离测试数据与测试逻辑,实现数据驱动测试。

Laputa 框架简介:Laputa 框架基于 Pytest 集成了对 API 接口自动化,以及对 Web 应用,移动端应用和 Windows 桌面应用 UI 等自动化的能力。具有可视化的 Web 界面工具,便于配置执行规则,关联执行脚本, 触发用例执行,查看执行结果。提供 CI 集成服务,调用 Jenkins API 跟踪持续集成结果,开放接口,实现流水线自动化测试。

3.1 环境依赖

3.2.1 参数化配置方式

pytest 参数化有两种方式:

@pytest.fixture(params=[])

@pytest.mark.parametrize()

两者都会多次执行使用它的测试函数,但 @pytest.mark.parametrize () 使用方法更丰富一些,laputa 更建议使用后者。

3.2.2 用 parametrize 实现参数化

parametrize ( ) 方法源码:

【python】
def parametrize(self,argnames, argvalues, indirect=False, ids=None, scope=None):

1. 主要参数说明

(1)argsnames :参数名,是个字符串,如中间用逗号分隔则表示为多个参数名。

(2)argsvalues :参数值,参数组成的列表,列表中有几个元素,就会生成几条用例。

2. 使用方法

(1)使用 @pytest.mark.paramtrize () 装饰测试方法;

(2)parametrize ('data', param) 中的 “data” 是自定义的参数名,param 是引入的参数列表;

(3)将自定义的参数名 data 作为参数传给测试用例 test_func;

(4)在测试用例内部使用 data 的参数。

创建测试用例,传入三组参数,每组两个元素,判断每组参数里面表达式和值是否相等,代码如下:

【python】
@pytest.mark.parametrize("test_input,expected",[("3+5",8),("2+5",7),("7*5",30)])
def test_eval(test_input,expected):
    # eval 将字符串str当成有效的表达式来求值,并返回结果
    assert eval(test_input) == expected

运行结果:

【python】
test_mark_paramize.py::test_eval[3+5-8]test_mark_paramize.py::test_eval[2+5-7] 
test_mark_paramize.py::test_eval[7*5-35]
============================== 3 passed in 0.02s ===============================

整个执行过程中,pytest 将参数列表 ("3+5",8),("2+5",7),("7*5",30) 中的三组数据取出来,每组数据生成一条测试用例,并且将每组数据中的两个元素分别赋值到方法中,作为测试方法的参数由测试用例使用。

3.2.3 多次使用 parametrize

同一个测试用例还可以同时添加多个 @pytest.mark.parametrize 装饰器,多个 parametrize 的所有元素互相组合(类似笛卡儿乘积),生成大量测试用例。

场景:比如登录场景,用户名输入情况有 n 种,密码的输入情况有 m 种,希望验证用户名和密码,就会涉及到 n*m 种组合的测试用例,如果把这些数据一一的列出来,工作量也是非常大的。pytest 提供了一种参数化的方式,将多组测试数据自动组合,生成大量的测试用例。示例代码如下:

【python】
@pytest.mark.parametrize("x",[1,2])@pytest.mark.parametrize("y",[8,10,11])
def test_foo(x,y):print(f"测试数据组合x: {x} , y:{y}")

运行结果:

【python】
test_mark_paramize.py::test_foo[8-1] 
test_mark_paramize.py::test_foo[8-2] 
test_mark_paramize.py::test_foo[10-1] 
test_mark_paramize.py::test_foo[10-2] 
test_mark_paramize.py::test_foo[11-1] 
test_mark_paramize.py::test_foo[11-2]

分析如上运行结果,测试方法 test_foo () 添加了两个 @pytest.mark.parametrize () 装饰器,两个装饰器分别提供两个参数值的列表,2 * 3 = 6 种结合,pytest 便会生成 6 条测试用例。在测试中通常使用这种方法是所有变量、所有取值的完全组合,可以实现全面的测试。

3.2.4 @pytest.fixture 与 @pytest.mark.parametrize 结合

下面讲讲结合 @pytest.fixture 与 @pytest.mark.parametrize 实现参数化。

如果测试数据需要在 fixture 方法中使用,同时也需要在测试用例中使用,可以在使用 parametrize 的时候添加一个参数 indirect=True,pytest 可以实现将参数传入到 fixture 方法中,也可以在当前的测试用例中使用。

parametrize 源码:

【python】
def parametrize(self,argnames, argvalues, indirect=False, ids=None, scope=None):

indirect 参数设置为 True,pytest 会把 argnames 当作函数去执行,将 argvalues 作为参数传入到 argnames 这个函数里。创建 “test_param.py” 文件,代码如下:

【python】
# 方法名作为参数
test_user_data = ['Tome', 'Jerry']
@pytest.fixture(scope="module")
def login_r(request):
    # 通过request.param获取参数
    user = request.param
    print(f"\n 登录用户:{user}")return user
@pytest.mark.parametrize("login_r", test_user_data,indirect=True)
def test_login(login_r):
    a = login_r
    print(f"测试用例中login的返回值; {a}")
    assert a != "

运行结果:

【plain】
登录用户:Tome PASSED            [50%]测试用例中login的返回值; Tome
登录用户:Jerry PASSED           [100%]测试用例中login的返回值; Jerry

上面的结果可以看出,当 indirect=True 时,会将 login_r 作为参数,test_user_data 被当作参数传入到 login_r 方法中,生成多条测试用例。通过 return 将结果返回,当调用 login_r 可以获取到 login_r 这个方法返回数据。

3.2.5 conftest 作用域

其作用范围是当前目录包括子目录里的测试模块。

(1)如果在测试框架的根目录创建 conftest.py 文件,文件中的 Fixture 的作用范围是所有测试模块。

(2)如果在某个单独的测试文件夹里创建 conftest.py 文件,文件中 Fixture 的作用范围,就仅局限于该测试文件夹里的测试模块。

(3)该测试文件夹外的测试模块,或者该测试文件夹外的测试文件夹,是无法调用到该 conftest.py 文件中的 Fixture。

(4)如果测试框架的根目录和子包中都有 conftest.py 文件,并且这两个 conftest.py 文件中都有一个同名的 Fixture,实际生效的是测试框架中子包目录下的 conftest.py 文件中配置的 Fixture。

3.3 代码 Demo

测试数据存储 yaml 文件:

【YAML】
测试流程:[
    {"name":"B2B普货运输三方司机流程","senior":{"createTransJobResource":"B2B","createType":"三方","platformType":2}},
    {"name":"B2B普货运输三方司机逆向流程","senior":{"isback":"True","createTransJobResource":"B2B","createType":"三方","platformType":2}},
  ]

测试数据准备,定义统一读取测试数据方法:

【python】
def dataBuilder(key):dires = path.join(dires, "test_data.yaml")
    parameters = laputa_util.read_yaml(dires)[key]
    name = []
    senior = []
    for item in parameters:
        name.append(item['name'] if 'name' in item else '')
        senior.append(item['senior'] if 'senior' in item else '')
    return name, senior

测试用例标识,通过 @pytest.mark.parametrize 方法驱动用例:

【python】
class TestRegression:
    case, param = dataBuilder('测试流程')
    @pytest.mark.parametrize("param", param, ids=case)
    def test_regression_case(self, param):
        # 调度
        res = create_trans_bill(params)
        trans_job_code = res['data']['jobcode']
        carrier_type = params['createType'] if params['createType'] in ('自营', '三方') else '个体'
        # 执行
        work_info = select_trans_work_info_new(trans_job_code)
        trans_work_code = work_info['trans_work_code']
        if 'isback' in params and params['isback']:
            execute_param.update(isBack=params['isback'])
        execute_bill_core(**execute_param)
        # 结算
        if carrier_type != '自营':
            trans_fee_code = CreateTransFeeBillBase.checkTF(trans_job_code)
    receive_trans_bill_core(**bill_param)

 

总结

日常测试过程中,无论是通过手动执行或者脚本执行,都需要利用数据驱动设计思路,这有助于提高测试场景覆盖率,测试用例的健壮性和复用性,及需求测试效率。通过数据驱动测试不仅可以得到更好的投资回报率,还可以达到质效合一的测试流程。

  • 【留下美好印记】
    赞赏支持
登录 后发表评论
+ 关注

热门文章

    最新讲堂

      • 推荐阅读
      • 换一换
          •   1、测试中,为什么是32个错误帧出现一次Busoff?  Busoff的产生是因为TEC(Transmit Error Counter)>255导致,再次提醒:与REC(Receive  Error Counter)无关。也就是说,如果节点状态切换到Busoff,是因为节点自身外发报文错误导致TEC>255。  回顾一下节点状态机,节点状态机如下所示:  在切入主题之前,对Error Passive状态做一个展开,节点由Error Active进入Error Passive,是因为REC>127 or TEC>127。所以,节点进入Error Passive状态的可以分两个层面看...
            0 0 170
            分享
          • 关于PandasPandas中的数据结构(1)Series:一维数组系列,也称序列;(2)DataFrame:二维的表格型数据结构;(3)Panel:三维数组。数据类型1.Logical(逻辑型)2. Numeric(数值型)3. Character(字符型)数据结构1.Series使用方法如下;Series([数据1,数据2,...],index=[索引1,索引2,...])例如:from pandas import Series X=Series(['a',2,'螃蟹'],index=[1,2,3]) X X[3]#访问inde...
            1 1 1523
            分享
          • Win10环境下搭建Monkey环境 明月别枝惊鹊,清风半夜鸣蝉!大家好,欢迎来到无界我的博客,最近做了手机APP测试,以下是我对环境安装的一个总结。一、Monkey是什么?Monkey 是一个命令行工具,可以运行在 Android 模拟器里或真实设备中。它可以向系统发送伪随机(pseudo-random)的用户事件流(如按键输入、触摸屏输入、手势输入等),可以对待测的目标应用或整个 Android 系统进行压力测试。因此 Monkey 测试是一种为了测试软件的稳定性、健壮性的快速有效的方法 二、Monkey在哪里?    每一台android手机里面都...
            13 13 1640
            分享
          •   软件测试工程师的前景怎么样?分享几个行业数据,用数据说话比较客观。(来源boss直聘)  从数据可以看出,目前从事软件测试行业的人中工作1~3年最多,工作3~5年后、工作5年以上的人很少。  测试这个行业还处于发展初期。因为如果后退10年,很少人知道软件测试是什么。直到今天,也有很多人不了解这个行业。  测试行业从业年龄一般在20至30岁之间,还比较年轻,年龄大的老测试,我佩服他们的学习能力。但是十年前的测试工具现在几乎都被新的框架所取代,如果不与时俱进地学习现在的新框架工具,就会面临被后浪淘汰的结果。软件测试行业平均收入  以北京为例,软件测试的平均工资现在是11366元/月,而我自己是...
            0 0 1022
            分享
          • 新浪科技讯 北京时间11月30日早间消息,据报道,Twitter表示,从11月23日起,该公司在最近的网站更新中已经不再推行与新冠肺炎不实信息相关的政策。这意味着Twitter将不再优先删除或标记与新冠肺炎有关的误导性信息。Twitter于2020年12月宣布,该公司将开始标记和删除与新冠肺炎疫苗有关的不实信息,原因是有成千上万的帐号发布与新冠病毒和疫苗接种的副作用有关的虚假信息。Twitter CEO埃隆·马斯克(Elon Musk)一直对美国卫生官员应对新冠疫情的方式颇有微词。他于2020年在播客 “The Joe Rogan Experience”上表示,美国新冠肺炎的死亡率远低于卫生官...
            0 0 584
            分享
      • 51testing软件测试圈微信