优化测试用例入参,重写测试夹具

This commit is contained in:
谈林海 2023-03-29 18:08:14 +08:00
parent 2018392521
commit 11d1ee3590
8 changed files with 82 additions and 58 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@ -5,7 +5,7 @@
* git地址: [https://gitee.com/t_l_h/t2-api-autotest.git](https://gitee.com/t_l_h/t2-api-autotest.git)
## 框架介绍
为了抛弃臃肿庞大的测试框架,本框架将大部分代码逻辑通过conftest文件实现前置,使得编写测试用例时无需导入各种乱七八糟的模块。
为了抛弃臃肿庞大的测试框架,本框架将大部分测试用例操作前置,使得编写测试用例时无需导入各种乱七八糟的模块。
## 框架功能
* yaml管理测试数据实现测试数据分离
@ -13,6 +13,7 @@
* 支持数据库的增、删、改、查
* 支持yaml文件中的动态参数自动替换
* 支持测试完成后发送钉钉消息通知
* 支持测试异常发送通知到邮箱
* 可轻易集成jenkins
@ -28,13 +29,14 @@
├── send_ding.py 钉钉消息通知
├── config.yml 全局配置
├── requirements.txt 依赖文件
└── Pipfile 虚拟环境依赖文件
├── Pipfile 虚拟环境依赖文件
└── logs 日志文件
```
### 使用教程
#### 1、Gitee 拉取项目
需要先配置好python、jdk、allure环境(不懂的自行百度)
需要先配置好python、jdk、allure环境
```shell
git clone https://gitee.com/t_l_h/t2-api-autotest.git
@ -71,6 +73,8 @@ exit # 退出虚拟环境
```
## 配置项目的通用配置
存放全局的一些配置,按需填写
![img.png](files/testconfig.png)
@ -106,43 +110,46 @@ exit # 退出虚拟环境
* allure.title: 用例名称
* pytest.mark.imports 用imports标记这条用例后续可以执行指定标记的用例
* pytest.mark.imports 用imports标记这条用例后续可以执行指定标记的用例也可以不填写会自动打上该测试用例所在模块的mark标记例如此用例在import模块下那会自动添加@pytest.mark.import标记
* pytest.mark.datafile: 需要使用的yaml测试数据需要从test_data目录开始写千万不能写错
* 测试函数必须test_*开头,重点说各个入参的字段
* 测试函数必须test_*开头,重点说测试用例的入参:
* requests: 封装好的实例化请求方法根据请求方式直接调用requests.request如图中的res所示
1、core: 可返回四个对象core.headers全局请求头、core.requests请求方法对象、core.sql数据库查询方法对象、 core.cache缓存方法对象
* ![img.png](files/core.png)
* env: 请求地址+请求方法组成的元祖数据使用时确保两边配置文件填写正确即可正常传入请求地址config.yml配置文件中的host以及对应测试用例中的path拼接请求方法对应测试用例中的method
2、env: 请求地址+请求方法组成的元祖数据使用时确保两边配置文件填写正确即可正常传入请求地址config.yml配置文件中的host以及对应测试用例中的path拼接请求方法对应测试用例中的method
3、case: 用例对应yaml文件中的case名称可在做不同校验时作为判断条件
* headers: 以静态方法存放在utils.requests_control文件的RestClient类中可按需更改
4、inputs: 用例对应yaml文件中inputs内容使用时直接用获取字典值的方式获取:
* case: 用例对应yaml文件中的case名称可在做不同校验时作为判断条件
* 如果是上传文件的接口可以参考前面截图中的样式填写只需要填写files目录下的文件名即可
* inputs: 用例对应yaml文件中inputs内容使用时直接用获取字典值的方式获取
1、如果是上传文件的接口可以参考前面截图中的样式填写只需要填写files目录下的文件名即可
2、如果请求参数需要动态参数需要用"{{xxx}}"这种双引号和双括号包裹的方式填写名称必须是utils.fake_data_control.py文件Execute类中定义的方法名可以按需添加
* 如果请求参数需要动态参数,需要用"{{xxx}}"这种双引号和双括号包裹的方式填写名称必须是utils.fake_data_control.py文件Execute类中定义的方法名可以按需添加
* expectation: 用例对应yaml文件中expectation内容使用时可以直接用字典值方式获取
* sql: 数据库实例化方法可以通过sql.query('xxxx'), sql.execute('xxxxx')方式调用使用时直接使用yaml文件中的sql语句sql.query(inputs['sql'])(当然也可以直接写)
### 接下来看看如何操作缓存数据:
* cache: 缓存依赖数据,使用方式如下
* 添加缓存数据:
![img.png](files/testcache.png)
#### 添加缓存数据:
cache.add_cache()方法把数据添加到test_data目录下的cache.yml文件中
调用cache.add_cache()方法把数据添加到test_data目录下的cache.yml文件中
![img.png](files/testcache.png)
* 使用缓存数据:
![img.png](files/testcache2.png)
#### 使用缓存数据:
约定在添加缓存数据时使用添加缓存数据的用例名称作为缓存的key值后续用例需要使用该key值时只需要在yaml文件中使用"$cache.xxxx"来替换缓存数据注意必须要用该格式填写其中xxxx部分为
添加缓存数据的key。例如上图中我在test_login.py用例中添加了key为test_login的缓存数据那我在test_import用例的yaml文件中就可以按如图所示方法使用需要用pytest.mark.run(order=1)装饰器保证添加缓存的用例执行必须在使用缓存的用例前)
![img.png](files/testcache2.png)
约定在添加缓存数据时使用添加缓存数据的用例名称作为缓存的key值后续用例需要使用该key值时只需要在yaml文件中使用"$cache.xxxx"来替换缓存数据注意必须要用该格式填写其中xxxx部分为
添加缓存数据的key。例如上图中我在test_login.py用例中添加了key为test_login的缓存数据那我在test_import用例的yaml文件中就可以按如图所示方法使用需要用pytest.mark.run(order=1)装饰器保证添加缓存的用例执行必须在使用缓存的用例前)
### 运行测试用例

View File

@ -9,7 +9,6 @@ host:
account:
password:
# jenkins相关配置
jenkins:
# 本地服务

View File

@ -1,10 +1,14 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2023/3/24 13:08
# @Author : 谈林海
import os
import pytest
from utils.allure_control import ReportStyle
from utils.path import root
from utils.log_control import logger
from utils.read_yaml_control import HandleYaml
from utils.allure_control import ReportStyle
from utils.data_handle_control import DataHandler, Config
from utils.requests_control import RestClient
@ -23,15 +27,14 @@ def pytest_generate_tests(metafunc):
data['inputs']['file'] = {'file': (
data['inputs']['file'], open(root / f"files/{data['inputs']['file']}", 'rb'),
'application/json')}
test_case = {
'case': data.get('case', {}),
'env': (str(host()) + str(test_data['common_inputs'].get('path', {})),
str(test_data['common_inputs'].get('method', ''))),
'inputs': data.get('inputs', {}),
'expectation': data.get('expectation', {})
}
test_case = {
'case': data.get('case', {}),
'env': (str(host()) + str(test_data['common_inputs'].get('path', {})),
str(test_data['common_inputs'].get('method', ''))),
'inputs': data.get('inputs', {}),
'expectation': data.get('expectation', {})
}
test_cases.append(test_case)
metafunc.parametrize(
"env, case, inputs, expectation",
[(tc['env'], tc['case'], tc['inputs'], tc['expectation']) for tc in test_cases],
@ -50,33 +53,45 @@ def alert_inputs(request):
@pytest.hookimpl
def pytest_assertion_pass(item, lineno, orig, expr=None, values=None):
"""测试报告显示断言内省"""
ReportStyle.allure_step_no(f'断言通过: {orig}')
def pytest_collection_modifyitems(items):
"""处理收集的测试用例"""
"""处理收集的测试用例"""
for item in items:
item.name = item.name.encode("utf-8").decode("unicode_escape")
item._nodeid = item.nodeid.encode("utf-8").decode("unicode_escape")
_marks = str(item.fspath).split('/')[-2]
_marks = str(item.fspath).split('/')[-2] # 测试用例对应模块
if item.get_closest_marker(name=f'{_marks}') is None:
item.add_marker(eval(f"pytest.mark.{_marks}"))
@pytest.fixture(autouse=True)
def collection(requests, headers, sql, cache):
class Core:
def __init__(self):
self.requests = requests
self.headers = headers
self.sql = sql
self.cache = cache
return Core()
@pytest.fixture()
def core(collection):
return collection
@pytest.fixture()
def headers():
"""通用请求头"""
yield RestClient().headers()
def host():
"""获取配置文件中的域名"""
res_host = HandleYaml(root / 'config.yml').read_yaml()['host']
return res_host
return RestClient().headers()
@pytest.fixture()
@ -95,8 +110,15 @@ def sql():
yield sql
@pytest.fixture(scope='session')
@pytest.fixture()
def cache():
"""返回一个字典,用作数据共享"""
yield {}
yield HandleYaml(root / 'test_data/cache.yml')
def host():
"""获取配置文件中的域名"""
res_host = HandleYaml(root / 'config.yml').read_yaml()['host']
return res_host

BIN
files/core.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 139 KiB

After

Width:  |  Height:  |  Size: 130 KiB

View File

@ -8,16 +8,12 @@ import pytest
@allure.title('XXX接口')
@pytest.mark.imports
@pytest.mark.datafile('test_data/import/test_import.yml')
def test_import(requests, env, headers, case, inputs, expectation, sql, cache):
print(requests,
env,
headers,
case,
inputs['file'],
expectation,
sql,
cache)
res = requests.request(env, headers=headers, files=inputs['file']).json()
def test_import(core, env, case, inputs, expectation):
# core.requests: 返回请求方法对象
# core.headers: 返回全局请求头
# core.sql: 返回查询方法对象
# core.cache: 返回缓存处理方法对象
res = core.requests.request(env, data=inputs['json'], headers=core.headers, files=inputs['file']).json()
assert res == expectation['response']

View File

@ -8,7 +8,7 @@ import pytest
@allure.title('登录接口')
@pytest.mark.login
@pytest.mark.datafile('test_data/login/test_login.yml')
def test_login(requests, env, headers, case, inputs, expectation, cache):
res = requests.request(env, json=inputs['json'], headers=headers).json()
cache.add_cache('test_login', res['key'])
def test_login(core, env, case, inputs, expectation):
res = core.requests.request(env, json=inputs['json'], headers=core.headers).json()
core.cache.add_cache('test_login', res['key'])
assert res == expectation['response']