优化测试用例入参,重写测试夹具
This commit is contained in:
parent
2018392521
commit
11d1ee3590
53
README.md
53
README.md
|
@ -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)装饰器保证添加缓存的用例执行必须在使用缓存的用例前)
|
||||
|
||||
### 运行测试用例
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@ host:
|
|||
account:
|
||||
password:
|
||||
|
||||
|
||||
# jenkins相关配置
|
||||
jenkins:
|
||||
# 本地服务
|
||||
|
|
64
conftest.py
64
conftest.py
|
@ -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
|
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 |
|
@ -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']
|
||||
|
||||
|
||||
|
|
|
@ -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']
|
||||
|
|
Loading…
Reference in New Issue