前言

公司突然要求你做自动化,但是没有代码基础不知道怎么做?或者有自动化基础,但是不知道如何系统性的做自动化, 放在yaml文件中维护不知道如何处理多业务依赖的逻辑

那么本自动化框架,将为你解决这些问题。

  • 框架主要使用 python 语言编写,结合 pytest 进行二次开发,用户仅需要在 yaml 或者 excel 文件中编写测试用例, 编写成功之后,会自动生成测试用例代码,零基础代码小白,也可以操作。
  • 如果是具备代码基础的,也可以直接通过 py 文件编写测试用例。
  • 使用 pytest-html / Allure 生成报告,并针对测试报告样式进行了调整,使得报告更加美观;
  • 测试完成后,支持发送 企业微信通知/ 钉钉通知/ 邮箱通知,灵活配置。

一、框架介绍

本框架主要是基于 Python + pytest + pytest-html/Allure + loguru + 邮件通知/企业微信通知/钉钉通知 实现的接口自动化框架。

对于框架任何问题,欢迎联系我!

二、实现功能

  • 通过session会话方式解决了登录之后cookie关联处理
  • 框架天然支持接口动态传参、关联灵活处理
  • 支持测试数据分析,测试数据不符合规范有预警机制
  • 测试数据隔离, 实现数据驱动
  • 自动生成用例代码: 测试人员在yaml/excel文件中填写好测试用例, 程序可以直接生成用例代码,纯小白也能使用
  • 动态多断言: 支持响应断言和数据库断言
  • 多种报告随心选择框架支持pytest-html以及Allure测试报告可以动态配置所需报告
  • 日志模块: 采用loguru管理日志可以输出更为优雅简洁的日志
  • 钉钉、企业微信通知: 支持多种通知场景,执行成功之后,可选择发送钉钉、或者企业微信、邮箱通知
  • 执行环境一键切换,解决多环境相互影响问题
  • 使用pipenv管理虚拟环境和依赖文件提供了一系列命令和选项来帮助你实现各种依赖和环境管理相关的操作

三、目录结构

├────case_utils/ 测试框架相关工具类
│    ├────__init__.py
│    ├────allure_handle.py  操作allure的相关方法
│    ├────platform_handle.py  跨平台的支持allure用于生成allure测试报告
│    ├────assert_handle.py  断言处理, 包括响应断言和数据库断言
│    ├────case_fun_handle.py   根据配置文件,从指定类型文件中读取用例数据,并调用生成用例文件方法,生成用例文件
│    ├────case_data_analysis  分析用例数据是否符合规范
│    ├────data_handle.py    数据处理
│    ├────request_data_handle.py   针对用例数据进行请求前后的处理
│    ├────get_results_handle.py   从pytest-html/allure测试报告中获取测试结果
│    └────send_result_handle.py   根据配置文件从html测试报告中获取测试结果发送指定类型的通知
├────common_utils/  公共的工具类
│    ├────__init__.py
│    ├────base_request.py   封装的requests请求
│    ├────bs4_handle.py  bs4BeautifulSoup4是Python中的第三方库。可以从HTML或XML文件中提取数据的Python库。
│    ├────dingding_handle.py   封装的钉钉机器人
│    ├────excel_handle.py  处理excel
│    ├────files_handle.py   处理文件相关操作
│    ├────func_handle.py   函数装饰器
│    ├────wechat_handle.py  封装企业微信机器人
│    ├────yagmail_handle.py  封装通过yagmail发送邮件的方法
│    ├────time_handle.py  封装处理时间操作的一些方法
│    └────yaml_handle.py  处理yaml文件
├────config/
│    ├────__init__.py
│    ├────case_template.txt  自动生成的测试用例文件模板
│    ├────global_vars.py    保存的一些全局变量
│    ├────project_path.py    项目路径管理
│    ├────report.css    优化html测试报告的样式文件
│    └────settings.py   配置文件
├────conftest.py
├────data/  测试用例数据
│    ├────test_login_demo.yaml
│    ├────test_login_excel_demo.xlsx
│    └────test_new_project_demo.yaml
├────outputs/
│    └────report/  保存测试报告的目录
│     └────log/   保存日志文件的目录
├────Pipfile
├────pytest.ini
├────README.md
├────run.py      运行入口  
└────test_case/   测试用例
│    ├────conftest.py
│    ├────test_auto_case/
│    │    ├────test_login_demo.py
│    │    ├────test_login_excel_demo.py
│    │    └────test_new_project_demo.py
│    └────test_manual_case/
│    │    ├────__init__.py
│    │    ├────test_demo.py
│    │    └────test_login_demo.py

四、依赖库

python_version = "3.9"
pymysql = "*"
loguru = "*"
requests-toolbelt = "*"
beautifulsoup4 = "*"
requests = "*"
openpyxl = "*"
sshtunnel = "*"
yagmail = "*"
pyyaml = "*"
click = "*"
faker = "*"
jsonpath = "*"
pytest = "==6.2.5"
pytest-html = "==2.1.1"
pytest-rerunfailures = "*"
allure-pytest = "==2.9.45"

五、安装教程

  1. 通过Git工具clone代码到本地 或者 直接下载压缩包ZIP
git clone https://gitlink.org.cn/floraachy/uiautotest.git
  1. 本地电脑搭建好 python环境我使用的python版本是3.9

  2. 安装pipenv

# 建议在项目根目录下执行命令安装
pip install pipenv
  1. 使用pipenv管理安装环境依赖包pipenv install (必须在项目根目录下执行)
   注意使用pipenv install会自动安装Pipfile里面的依赖包该依赖包仅安装在虚拟环境里不安装在测试机。

如上环境都已经搭建好了,包括框架依赖包也都安装好了。

六、如何创建用例

1. 修改配置文件 config.settings.py

1确认用例是通过YAML还是Excel编写由CASE_FILE_TYPE控制 2确认测试完成后是否发送测试结果由SEND_RESULT_TYPE控制并填充对应邮件/钉钉/企业微信配置信息 3确认测试是否需要进行数据库断言如有需求填充数据库配置信息 4指定日志收集级别由LOG_LEVEL控制

2. 修改全局变量,增加测试数据 config.global_vars.py

  1. ENV_VARS["common"]是一些公共参数,如报告标题,报告名称,测试者,测试部门。后续会显示在测试报告上。如果还有其他,可自行添加 2ENV_VARS["test"]是保存test环境的一些测试数据。ENV_VARS["live"]是保存live环境的一些测试数据。如果还有其他环境可以继续增加例如增加ENV_VARS["dev"] = {"host": "", ......}

3. 删除框架中的示例用例数据

1删除 data目录下所有的YAML和EXCEL文件 2删除 test_case/test_manual_case目录下所有手动编写的用例

4. 编写测试用例(两种方式任选其一或者都选)

1 自动生成测试用例 data test_case.test_auto_case

  • 在目录data下新建一个YAML/Excel文件。按照如下字段要求进行测试用例数据添加
  • 注意如果需要自动创建测试用例文件YAML/Excel文件的文件名需要以"test"开头。

2. 手动编写测试用例 data test_case.test_manual_case

  • 原则上如果是手动编写测试用例python代码 测试用例数据文件不要以"test"开头。 如果以“test”开头可能导致用例运行多次。 1在目录data下新建一个YAML/Excel文件按照要求编写测试用例数据 2在test_case.test_manual_case下新建一个以"test"开头的测试方法,进行测试用例方法编写。

5. 用例中相关字段的介绍

- case_common :公共参数
	- allure_epic用作于@allure.epic()装饰器中的内容。如果是使用Excel管理用例数据这个目前是写死的。可在case_utils.case_handle.py中更改。
	- allure_feature用作于@allure.feature()装饰器中的内容。如果是使用Excel管理用例数据 这个用的是excel的表单名称。
	- allure_story用作于@allure.story()装饰器中的内容。如果是使用Excel管理用例数据 这个用的是excel的表单名称。
- case_info用例数据
	- feature用例所属模块 类似于@allure.feature()。
	- title用例标题
	- run是否执行用例为空或者True都会执行为False则不执行。
	- url请求路径可填写全路径 或者 资源路径。通常我们填写资源路径。在用例执行前会针对路径进行处理。具体可见case_utils.request_data_handle.py.RequestPreDataHandle.url_handle。注请求路径=基准路径host/base_url+资源路径(url)。
	- method请求方式例如GET, POST, DELETE, PUT, PATCH等
	- headers请求头注意如果在headers里面防止cookies其值类型需要是字符串
	- cookies请求cookies格式是DICT CookieJar对象
	- request_type请求数据类型params, json, file, data
	- payload请求参数
	- files上传附件接口所需的文件绝对路径
	- extract后置提取参数
	- assert_response响应断言
	- assert_sql数据库断言

6. Excel用例单独说明

框架支持excel多表单自动生成测试用例每一个表单作为一个测试用例模块。 例如: excel表格名称是test_demo.xlsx excel表单1名称是GitLink-登录模块 excel表单2名称是示例模块

生成规则:

  • 如果excel表单中存在"-",我们将取"-"后面的部分的首字母拼接excel文件名称作为测试用例模块/测试用例类/测试用例方法名称
  • 如果excel表单中不存在"-"我们将直接获取表单名称首字母拼接excel文件名称作为测试用例模块/测试用例类/测试用例方法名称
  • 测试用例模块/测试用例类/测试用例方法名称同时也将遵循python语法规则进行适当调整

基于上述规则:

  • excel第一个表单生成的测试用例 测试用例模块test_demo_dlmk.py 测试用例类TestDemoDlmkAuto 测试用例方法test_demo_dlmk_auto

  • excel第二个表单生成的测试用例 测试用例模块test_demo_slmk.py 测试用例类TestDemoSlmkAuto 测试用例方法test_demo_slmk_auto

六、运行自动化测试

1. 激活已存在的虚拟环境

  • 如果不存在会创建一个pipenv shell (必须在项目根目录下执行)

2. 运行

在pycharm>terminal或者电脑命令窗口进入项目根路径执行如下命令如果依赖包是安装在虚拟环境中需要先启动虚拟环境。
  > python run.py  (默认在test环境运行测试用例, 报告采用allure)
  > python run.py -env live 在live环境运行测试用例
  > python run.py -env=test 在test环境运行测试用例
  > python run.py -report=pytest-html (默认在test环境运行测试用例, 报告采用pytest-html)

注意:

  • 如果pycharm.interpreter拥有了框架所需的所有依赖包可以通过pycharm直接在run.py中右键运行

七、查看测试报告

pytest-html测试报告

如果是pytest-html生成的测试报告直接打开outputs目录下的.html报告即可。支持通过任意浏览器打开查看

Allure测试报告

  1. 如果是Allure生成的测试报告支持通过pycharm点击outputs/report/allure_html/index.html打开查看测试报告
  2. 如果不通过pycharm打开直接通过文件夹打开windows系统环境下可以点击outputs/report/allure_html/双击打开Allure报告.bat打开查看测试报告

注意:

  • 通过点击outputs/report/allure_html/双击打开Allure报告.bat打开测试报告的方法暂时不支持mac系统
  • 如果通过点击outputs/report/allure_html/双击打开Allure报告.bat打开测试报告,命令窗口显示乱码,或者打不开,可以把.bat的文件名称修改为英文的名称,里面的所有中文注释全部移除,再次尝试

八 、详细功能说明

1. 用例中如何生成随机数据

在测试过程中,可能涉及到一些特殊场景,需要生成定制化的数据。每次运行测试,都需要按照指定规则随机生成。 例如:data/test_new_project_demo.yaml 中payload.name就是使用Faker随机生成的。 我这里写了一个表达式:${faker.name().replace(" ", "").replace(".", "")}

  • 在测试方法中case_data = RequestPreDataHandle(case).request_data_handle()会进行用例数据处理
  • 处理过程中会调用common_utils.data_handle.data_replace进行数据处理
  • common_utils.data_handle 有导入Faker包并且初始化了faker对象。因此上述表达式能成功运行获取其结果

这里需要注意以下几点:

  • 如果是python自带的一些方法不需要额外导包或者写方法就会直接处理。
  • Faker这个Python库已经可以满足生成各种各样的伪数据这个我已经在common_utils.data_handle 中定义好了。
  • 如果还有一些其他的定制化数据,可以在common_utils.data_handle 中进行添加。

2. 用例中如何提取响应数据作为全局变量并使用

在测试过程中,通常下一个接口需要用到上一个接口的响应数据,这个时候就涉及到参数的提取。 我们在用例数据中定义了参数:extract进行后置参数的提取根据接口返回数据的类型JSON或者Text采取不同的方法从响应数据中提取参数保存在全局变量中。 例如:

  • 登录接口中定义了需要提取的参数:data/test_login_demo.yaml
  extract:
    nickname: $.username
    login: $.login
    user_id: $.user_id
  • 请求结束后,case_utils/request_data_handle.py.after_request_extract就会接口返回数据的类型JSON或者Text采取不同的方法从响应数据中提取参数保存在全局变量中。
  • 下一个接口需要用到user_id只需要在用例中以如下格式书写${user_id}即可。
  payload:
    "user_id": ${user_id}
    "name": ${faker.name().replace(" ", "").replace(".", "")}
    "repository_name": ${faker.name().replace(" ", "").replace(".", "")}

3. 如何进行响应数据断言

以下是支持的几种响应断言:

断言方式 说明
eq 相等,判断预期结果是否等于实际结果
in 包含, 判断实际结果是否包含预期结果
gt 大于, 判断预期结果是否大于实际结果
lt 小于, 判断预期结果是否小于实际结果
not 非,判断预期结果不等于实际结果

以下是响应断言示例:

  assert_response:
    eq:
      http_code: 200
      $.user_id: ${user_id}
    in:
      $.login: ${login}
    gt:
      $.user_id: 84955
    lt:
      $.user_id: 84953
    not:
      $.user_id: 85390
  • 预期结果http_code实际结果200 会从接口获取响应码,判断预期结果是否等于实际结果。
  • 预期结果:$.user_id 实际结果:${user_id} 会从接口响应数据中通过表达式$.user_id提取user_id作为预期结果 从全局变量中替换变量${user_id}获取user_id作为实际结果判断预期结果是否等于实际结果。
  • 预期结果:$.login 实际结果:${login} 会从接口响应数据中通过表达式$.login提取login作为预期结果 从全局变量中替换变量${login}获取login作为实际结果判断实际结果是否包含预期结果。
  • 预期结果:$.user_id 实际结果84955 会从接口响应数据中通过表达式$.user_id提取user_id作为预期结果判断预期结果是否大于实际结果。
  • 预期结果:$.user_id 实际结果84953 会从接口响应数据中通过表达式$.user_id提取user_id作为预期结果判断预期结果是否小于实际结果。
  • 预期结果:$.user_id 实际结果85390 会从接口响应数据中通过表达式$.user_id提取user_id作为预期结果判断预期结果是否不等于实际结果。

4. 如何进行数据库断言

以下是支持的几种数据库断言:

断言方式 说明
len 数据库SQL查询结果的数量 是否 等于预期结果
eq 从数据库SQL查询结果中通过jsonpath表达式提取值判断是否等于预期结果
...... 其他断言方式待扩展

以下是数据库断言示例-1

  assert_sql:
    eq:
      sql: select count(*) from tokens where user_id=${user_id};
      len: 1
  • sql表示需要查询的SQL 这里调用的是common_utils/mysql_handle.py.MysqlServer.query_one, 返回的数据类型是字典。
  • len:1 判断的是查询结果的个数是否等于1.
  • 该场景一般用于某操作往数据库中插入了一条数据,判断是否插入成功,而不需要去校验其数据准确性。
  • 例如上述数据库断言示例中我是去查询tokens中是否存在指定user_id的数据其实际场景是只要登录成功就会往token表里面插入登录用户的token。因此要判断实际是否登录成功只需要判断表里面有没有针对该用户插入一条数据即可。

以下是数据库断言示例-2

  assert_sql:
    eq:
      sql: select id, `name`, identifier from projects where user_id=${user_id} ORDER BY created_on DESC;
      $.id: ${project_id}
      $.name: ${project_name}
      $.identifier: ${project_identifier}
  • sql表示需要查询的SQL 这里调用的是common_utils/mysql_handle.py.MysqlServer.query_one, 返回的数据类型是字典。
  • 这里是在projects表里面查询指定用户创建的项目信息id, name, identifier并按创建时间倒序排序。
  • 预期结果:$.id 实际结果:${project_id} 从数据库查询结果中通过表达式$.id提取id作为实际结果 从全局变量中替换变量${project_id}获取project_id作为实际结果判断预期结果是否等于实际结果。
  • 预期结果:$.name 实际结果:${project_name} 从数据库查询结果中通过表达式$.name提取name作为实际结果 从全局变量中替换变量${project_name}获取project_name作为实际结果判断预期结果是否等于实际结果。
  • 预期结果:$.identifier 实际结果:${project_identifier} 从数据库查询结果中通过表达式$.identifier提取identifier作为实际结果 从全局变量中替换变量${project_identifier}获取project_identifier作为实际结果判断预期结果是否等于实际结果。

注意:

  • 关于数据库断言,需要考虑实际使用场景,来综合考虑调整。这里我考虑的可能还有局限性。欢迎大家来反馈。
  • 另外其实sql有很大程度影响数据库断言的走向 我们写sql的时候尽量写的精准一些。

5. 配置邮箱通知

  • 首先我们需要在配置文件config/settings.py中选择邮件发送方式SEND_RESULT_TYPE = 3
  • 获取邮件的相关信息,并填写到配置文件config/settings.py中。这些配置信息可以从邮箱设置中获取。不知道如何配置的,可以直接互联网上搜索。
# 发送邮件的相关配置信息
email = {
    "user": "******",  # 发件人邮箱
    "password": "******",  # 发件人邮箱授权码/发件人邮箱密码
    "host": "smtp.qq.com",
    "to": ["******", "******"]  # 收件人邮箱
}

  • 在配置文件config/settings.py中配置邮件发送的标题以及内容。 注意: 1以下${变量名}是已经定义好的。只能减,不能增。 2邮件标题以及内容也可自行调整。
# ------------------------------------ 邮件通知内容 ----------------------------------------------------#
email_subject = f"接口自动化报告"
email_content = """
           各位同事, 大家好:

           自动化用例于 <strong>${start_time} </strong> 开始运行,运行时长:<strong>${run_time} s</strong> 目前已执行完成。
           ---------------------------------------------------------------------------------------------------------------
           测试人:<strong> ${tester} </strong> 
           所属部门:<strong> ${department} </strong>
           项目环境:<strong> ${run_env} </strong>
           ---------------------------------------------------------------------------------------------------------------
           执行结果如下:
           &nbsp;&nbsp;用例运行总数:<strong> ${total} 个</strong>
           &nbsp;&nbsp;通过用例个数passed: <strong><font color="green" >${passed} 个</font></strong>
           &nbsp;&nbsp;失败用例个数failed: <strong><font color="red" >${failed} 个</font></strong>
           &nbsp;&nbsp;异常用例个数error: <strong><font color="orange" >${broken} 个</font></strong>
           &nbsp;&nbsp;跳过用例个数skipped: <strong><font color="grey" >${skipped} 个</font></strong>
           &nbsp;&nbsp;失败重试用例个数 * 次数之和rerun: <strong>${rerun} 个</strong>
           &nbsp;&nbsp;成  功   率:<strong> <font color="green" >${pass_rate} %</font></strong>

           **********************************
           附件为具体的测试报告,详细情况可下载附件查看, 非相关负责人员可忽略此消息。谢谢。
       """

6. 配置钉钉通知

  • 首先我们需要在配置文件config/settings.py中选择钉钉发送方式SEND_RESULT_TYPE = 1
  • 获取钉钉的相关信息,并填写到配置文件config/settings.py中。具体可以参考:钉钉机器人
# ------------------------------------ 钉钉相关配置 ----------------------------------------------------#
ding_talk = {
    "webhook_url": "https://oapi.dingtalk.com/robot/send?access_token=***********",
    "secret": "***********"
}

  • 在配置文件config/settings.py中配置钉钉发送的标题以及内容。 注意: 1以下${变量名}是已经定义好的。只能减,不能增。 2邮件标题以及内容也可自行调整。
# ------------------------------------ 钉钉通知内容 ----------------------------------------------------#
ding_talk_title = f"接口自动化报告"
ding_talk_content = """
           各位同事, 大家好:

           ### 自动化用例于 ${start_time} 开始运行,运行时长:${run_time} s 目前已执行完成。
            ---------------------------------------------------------------------------------------------------------------
           #### 测试人: ${tester}
           #### 所属部门: ${department}
           #### 项目环境: ${run_env} 
           ---------------------------------------------------------------------------------------------------------------
           #### 执行结果如下:
           - 用例运行总数: ${total}           - 通过用例个数passed: ${passed}           - 失败用例个数failed: ${failed}           - 异常用例个数error: ${broken}           - 跳过用例个数skipped: ${skipped}           - 失败重试用例个数 * 次数之和rerun: ${rerun}           - 成  功   率: ${pass_rate} %

           **********************************
           附件为具体的测试报告,详细情况可下载附件查看, 非相关负责人员可忽略此消息。谢谢。
       """

7. 配置企业微信通知

  • 首先我们需要在配置文件config/settings.py中选择企业微信发送方式SEND_RESULT_TYPE = 2
  • 获取企业微信的相关信息,并填写到配置文件config/settings.py中。具体可以参考:企业微信
# ------------------------------------ 企业微信相关配置 ----------------------------------------------------#
wechat = {
    "webhook_url": "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=********",
}
  • 在配置文件config/settings.py中配置企业微信发送内容。 注意: 1以下${变量名}是已经定义好的。只能减,不能增。 2邮件标题以及内容也可自行调整。
# ------------------------------------ 企业微信通知内容 ----------------------------------------------------#
wechat_content = """
           各位同事, 大家好:

           ### 自动化用例于 ${start_time} 开始运行,运行时长:${run_time} s 目前已执行完成。
           --------------------------------
           #### 测试人: ${tester}
           #### 所属部门: ${department}
           #### 项目环境: ${run_env} 
           --------------------------------
           #### 执行结果如下:
           - 用例运行总数: ${total}           - 通过用例个数passed:<font color=\"info\"> ${passed} 个</font>
           - 失败用例个数failed: <font color=\"warning\"> ${failed}  个</font>
           - 异常用例个数error: <font color=\"warning\"> ${broken} 个</font>
           - 跳过用例个数skipped: <font color=\"comment\"> ${skipped} 个</font>
           - 失败重试用例个数 * 次数之和rerun: <font color=\"comment\"> ${rerun} 个</font>
           - 成  功   率: <font color=\"info\"> ${pass_rate} % </font>

           **********************************
           附件为具体的测试报告,详细情况可下载附件查看, 非相关负责人员可忽略此消息。谢谢。
       """

初始化项目可能遇到的问题

1. 测试机安装的是python3.7但是本框架要求3.9.5,怎么办?

方法一:建议采纳此方法 1首先在项目根目录下打开命令窗口移除虚拟环境pipenv --rm 2安装虚拟环境时忽略锁定的版本号同时安装依赖包pipenv install --skip-lock 如果使用上述命令报错Warning: Python 3.9 was not found on your system... Neither 'pyenv' nor 'asdf' could be found to install Python. 请使用如下命令pipenv install --python 3.7 --skip-lock (注意这里的版本号如果你的是3.8就应该如下写命令pipenv install --python 3.8 --skip-lock)

3激活虚拟环境pipenv shell

4运行框架python run.py


方法二: 1首先在项目根目录下打开命令窗口移除虚拟环境pipenv --rm 2更改项目根目录下的Pipfile文件

# 如下所示3.9更改为3.7
[requires]
python_version = "3.7"

3更改项目根目录下的Pipfile.lock文件

# 如下所示3.9更改为3.7
        "requires": {
            "python_version": "3.7"
        },

4安装虚拟环境同时安装依赖包pipenv install

5激活虚拟环境pipenv shell

6运行框架python run.py

2. 无法安装依赖包或者安装很慢,怎么办?

检查一下Pipfile文件中的pip的安装源位置Pipfile 以下安装源均可:

pip默认的镜像地址是https://pypi.org/simple
清华大学https://pypi.tuna.tsinghua.edu.cn/simple 清华大学的pip源是官网pypi的镜像每隔5分钟同步一次重点推荐

阿里云http://mirrors.aliyun.com/pypi/simple/

中国科技大学 https://pypi.mirrors.ustc.edu.cn/simple/

华中理工大学http://pypi.hustunique.com/

山东理工大学http://pypi.sdutlinux.org/

豆瓣http://pypi.douban.com/simple/
Description
No description provided
Readme ZPL-2.0 35 MiB
Languages
Python 85%
JavaScript 8.1%
Shell 4.6%
Batchfile 1.8%
CSS 0.5%