...

案例4 自动化用例收集、重命名、生成

案例需求

假设你们有一套基于qtaf的多人合作测试框架,已经积累了很多测试用例,但是随着用例的增多,发现一些问题:

  1. 需要统计下每个模块、每个人的用例数量
  2. 最开始的用例优先级规划较为混乱,需要重新规划,需要你把当前所有用例整理出来(Excel或CSV)
  3. 有些用例脚本文件名和其中的测试类名不统一
  4. 有些尚未实现功能的用例脚步需要在测试框架中标记出来
  5. 有些用例文件名带✅/❌等执行状态,需要清理掉
  6. 用例生成:在功能测试结束后,需要根据Excel中的功能用例,编写规范格式的自动化用例

已知,所有用例都在测试框架的testcases目录下,目录结构如下:

testcases/
  模块1
    __init__.py
    子模块1-1
      __init__.py
      aaaa.py  # 用例脚本
      bbbb.py  # 用例脚本
      子子模块1-1-1
         __init__.py
        cccc❌.py  # 用例脚本
        dddd✅.py  # 用例脚本
  模块2
  ....

已知一个脚本仅一个测试用例,格式参考如下

# 文件名demo.py
from testbase import TestCase


class DemoTestCase(TestCase):     # 用例名(一个类一个用例)
    """基于qtaf的用例示例"""            # 用例标题
    desc = ''                                  # 用例描述(可能没有该属性)
    owner = 'superhin'                     # 归属人
    priority = TestCase.EnumPriority.Low   # 优先级 
    status = TestCase.EnumStatus.Ready  # 用例状态
    tags = "demo", "contract-manage"     # 用例标签(可能没有该属性, 可能是str, tuple或set类型)
    timeout = 1                              # 超时时间

    def pre_test(self):                     # setup步骤(可能没有)
        """                                      # 预置条件描述(可能没有)
        1. 预置条件1                          
        1. 预置条件2
        """
        # ..具体步骤实现


    def run_test(self):                     # 固定用例运行方法
        """                                      # 测试步骤及期望结果描述(可能没有)
        测试步骤:
        1. 测试步骤1
        2. 测试步骤2
        3. 测试步骤3

        期望结果
        2. 期望结果2
        3. 期望结果3

        """
        self.start_step('测试步骤1')     # 每个步骤的开始(可能没有)
        # ...步骤具体实现

        self.start_step('测试步骤2')
        # ...步骤具体实现
        
        self.start_step('测试步骤3')
        # ...步骤具体实现


if __name__ == '__main__':
    DemoTestCase().debug_run()

请实现以下功能

  1. 编辑一个脚本,统计出每个模块用例数,每个人的用例数量。
  2. 编写一个脚本,批量移除脚本后的✅或❌,执行状态标记。
  3. 如果测试用例脚本中类名与脚本名不一致,修改代码类名与脚本名一致,由脚本名(蛇型命名)转为类命名(大驼峰法)
  4. 编写一个脚本,如果当前脚本状态为实现中(TestCase.EnumStatus.Implement),且文件名不以_implement.py结尾,则将脚本文件名xxx.py改为xxx_implement.py
  5. 根据excel用例,在某个子模块下指定目录,下批量生成待实现(status =TestCase.EnumStatus.Implement)的测试用例脚本(标准格式参考上面的demo.py)
  6. 编写一个脚本,导出所有的用例成Excel或CSV, 包括 '用例名称(测试类名), '标题'(测试类描述), '描述'(desc属性), '标签'(tags), '状态' (status), '优先级' (priority), '归属人' (owner), '模块' (testcases下一级子目录), '子模块' (模块下一级子目录), '路径'(相对于testcases的脚本路径或模块路径) , '步骤' (步骤描述)等。
用例ID 用例标题 目录 优先级 预置条件 测试步骤 期望结果 归属人 创建时间
1000023 基于qtaf的用例示例 sub_dir1/demo 1.预置条件1\n2.预置条件2 1.测试步骤1\n2.测试步骤2\n3.测试步骤3 2.期望结果2\n3.期望结果3 临渊 2022.12.12 12:00:00

提示

练习重点
文件及目录相关操作:

  • open() # 读写.py(纯文本)文件
  • os.walk(): 参考 # 目录遍历
  • os.rename(): 参考 # 重命名文件
  • os.makedirs(): 参考 # 生成目录及子目录
  • os.path: 参考 # 文件路径相关]
    • os.path.dirname() # 上级路径
    • os.path.basename() # 文件名
    • os.path.join() # 路径连接
    • os.path.exists() # 是否存在
    • os.path.isfile() # 是否文件
    • os.path.isdir() # 是否目录
  • sort()itertools.groupby(): 参考 # 分组统计(按模块分组/按用例归属人分组/按优先级分组)
  • csvexcel读写

难点

  • 目录遍历os.walk()的使用及路径组装
  • 生成用例时代码模板的组装,可以逐个部分(步骤等)组装,也可以使用Jinja2模板引擎(支持{% for ...%}循环)
  • 代码洞察:从代码中提取类名、类注释(docstring)、步骤描述等
  • 正则匹配

提示
使用inspect代码洞察的实现方法参考

已知: 1. 用例目录testcases下所有的模块、子模块、子目录等都以包的形式存在(即目录下都有__init__.py),可以按路径导入模块
2. 所有模块、子模块、子目录名称都是合法的模块名(仅包含字母数字下划线)

  1. os.walk()时,组装出脚本相对于testcases的完整路径 (os.path.join() 路径组装)
  2. 根据脚本testcases/module1/sub_module1_1/demo.py 得到模块导入路径testcases.module1.sub_module1_1.demo (字符串替换)
  3. 导入模块,得到模块对象
import testcases.module1.sub_module1_1.demo as test_module  # 给个别名方便使用

或使用Python内置库importlib

import importlib

test_module = importlib.import_module('testcases.module1.sub_module1_1.demo')
  1. 使用Python内置库inspect获取模块中所有的类名
import importlib
import inspect

# 模块对象
test_module = importlib.import_module('testcases.module1.sub_module1_1.demo')

classes = []  # 新建一个列表,保存改模块中的所有类名
cls_members = inspect.getmembers(test_module, inspect.isclass)  # test_module是导入的模块对象
for (cls_name, cls) in cls_members:  # cls_name为类名,cls为类对象,包含导入的类
    classes.append(cls)  # todo 过滤导入的类

test_class = classes[0]  # 这里取第一个类名,不严谨

  1. 根据类对象获取类名、类注释、类属性
test_class_doc = test_class.__doc__  # 获取类注释(即用例标题)
status = getattr(test_class, 'status') # 获取类status属性(即用例状态,必填属性也可以直接用test_class.status获取)
priority = getattr(test_class, 'priority')  # 用例优先级
owner = getattr(test_class, 'owner')   # 用例归属人
desc = getattr(test_class, 'desc', '')   # 用例描述,获取不到时,设置默认值为空字符串
# ...

  1. 获取测试方法对象注释、代码等
test_method = getattr(test_class, 'run_test')  # 获取测试方法对象(已知,类中的测试方法名为固定的run_test方法)
test_method_doc = test_method.__doc__ # 测试方法注释(步骤描述、预期结果描述等,可能为空)
test_method_code = inspect.getsource(test_method)  # 得到方法源代码,测试方法注释中步骤描述为空时,考虑从源代码通过正则匹配self.start_step()中的内容,得到所有步骤描述
# 判断及parse_steps
posted @ 2023-06-05 16:33  韩志超  阅读(170)  评论(0编辑  收藏  举报