!1 合并最新代码

Merge pull request !1 from 遥爸/jack
This commit is contained in:
遥爸 2023-04-11 14:21:51 +00:00 committed by Gitee
commit 8fa5930e90
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
16 changed files with 322 additions and 25 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -62,4 +62,6 @@ lark:
webhook:
# 定义重跑参数
reruns: 3
reruns_delay: 3

View File

@ -0,0 +1,48 @@
case_common:
allureEpic: 上传通用接口
allureFeature: 上传接口
allureStory: 上传接口
upload_file_01:
host: http://test-admin-api.jekunauto.net
url: /v1/upload/create
method: POST
detail: 上传接口
headers:
Content-Type: 'multipart/form-data'
Authorization: 'Jekun 0013936:JHC9i8pAh3:zec96hsUSd0v17q9eM2x0WEy740='
Entity-Date: 'Fri, 24 Feb 2023 07:23:48 GMT'
User-Agent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36'
# 这里cookie的值写的是存入缓存的名称
# 请求的数据,是 params 还是 json、或者file、data
requestType: file
# 是否执行,空或者 true 都会执行
is_run:
data:
file:
file: '排入水体名.png'
data:
act: '232'
params:
fields: '*'
dependence_case: False
# 依赖的数据
dependence_case_data:
- case_id:
dependent_data:
- dependent_type:
jsonpath:
set_cache:
assert:
# 断言接口状态码
status_code: 200
data:
jsonpath: $.data.act
type: ==
value: '232'
AssertType:
sql:

View File

@ -0,0 +1,154 @@
case_common:
allureEpic: JekunAuto小程序接口
allureFeature: 商城模块
allureStory: 商城模块
platform_index_01:
host: ${{host()}}
url: /v1/category/platform-index
method: GET
detail: 商城主页
headers:
Connection: 'keep-alive'
clientSource: '0'
areaCode: '440105'
content-type: 'application/json'
Cookie: '_csrf-frontend=d096dba5839b9b40dbe5272fc4e4957f6b1b184d8fb7eaebc21cd75627ef2bffa%3A2%3A%7Bi%3A0%3Bs%3A14%3A%22_csrf-frontend%22%3Bi%3A1%3Bs%3A32%3A%2293xDpdFnQnR8bB2icCtVFXIALxa_KJzO%22%3B%7D; advanced-frontend=m5ls5co6orfgd1r2147cild98g'
# 这里cookie的值写的是存入缓存的名称
# 请求的数据,是 params 还是 json、或者file、data
requestType: params
# 是否执行,空或者 true 都会执行
is_run:
data:
fields: "id,categoryName,childCategories"
thirdPartySource: JEKUNAUTO
clientSource: "0"
dependence_case: False
# 依赖的数据
dependence_case_data:
- case_id: user_info_01
dependent_data:
- dependent_type: response
jsonpath: $.data[userId]
set_cache: userId
- dependent_type: response
jsonpath: $.data[accessId]
set_cache: accessId
- dependent_type: response
jsonpath: $.data[accessKey]
set_cache: accessKey
assert:
# 断言接口状态码
status_code: 200
data:
jsonpath: $.data[0].categoryName
type: ==
value: "车身护理"
AssertType:
sql:
platform_property_02:
host: ${{host()}}
url: /v1/service/service/property
method: get
detail: 获取商城筛选字段
headers:
Connection: 'keep-alive'
clientSource: '0'
areaCode: '440105'
content-type: 'application/json'
requestType: params
is_run:
data:
fields: "goodsProperty,isNeedMatchCar,matchProperty,sort"
thirdPartySource: JEKUNAUTO
carLicense:
frontServiceId: 1
dependence_case:
dependence_case_data:
assert:
status_code: 200
data:
jsonpath: $.data.*
type: contains
value: "价格从高至低"
AssertType:
sql:
platform_goods_list_03:
host: ${{host()}}
url: /v1/service/service/platform-goods-list
method: get
detail: 获取商品详情
headers:
Connection: 'keep-alive'
clientSource: '0'
areaCode: '440105'
content-type: 'application/json'
requestType: params
is_run:
data:
fields: "goods,after"
thirdPartySource: JEKUNAUTO
carLicense: "粤AAAA02"
frontServiceId: 1
goodsPropertyItemIds: []
dependence_case:
dependence_case_data:
assert:
status_code: 200
sql:
platform_goods_comment_04:
host: ${{host()}}
url: /v1/order/order-goods-comment/latest
method: get
detail: 商品最新评价
headers:
Connection: 'keep-alive'
clientSource: '0'
areaCode: '440105'
content-type: 'application/json'
from: 1082943R5J4DKfcE
requestType: params
is_run:
data:
fields: "*"
thirdPartySource: JEKUNAUTO
goodsId: "1"
dependence_case:
dependence_case_data:
assert:
status_code: 200
sql:
platform_goods_total_05:
host: ${{host()}}
url: /v1/order/order-goods-comment/latest
method: get
detail: 获取商品total
headers:
Connection: 'keep-alive'
clientSource: '0'
areaCode: '440105'
content-type: 'application/json'
from: 1082943R5J4DKfcE
requestType: params
is_run:
data:
fields: "*"
thirdPartySource: JEKUNAUTO
goodsId: "1"
dependence_case:
dependence_case_data:
assert:
status_code: 404
sql:

View File

@ -179,5 +179,4 @@ user_info_car_01:
assert:
# 断言接口状态码
status_code: 200
sql:
sql:

View File

@ -0,0 +1,39 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2023-03-10 11:03:09
import allure
import pytest
from utils.read_files_tools.get_yaml_data_analysis import GetTestCase
from utils.assertion.assert_control import Assert
from utils.requests_tool.request_control import RequestControl
from utils.read_files_tools.regular_control import regular
from utils.requests_tool.teardown_control import TearDownHandler
case_id = ['upload_file_01']
TestData = GetTestCase.case_data(case_id)
re_data = regular(str(TestData))
@allure.epic("上传通用接口")
@allure.feature("上传接口")
class TestUploadFile:
@pytest.mask.flaky(reruns=3, reruns_delay=3)
@allure.story("上传接口")
@pytest.mark.parametrize('in_data', eval(re_data), ids=[i['detail'] for i in TestData])
def test_upload_file(self, in_data, case_skip):
"""
:param :
:return:
"""
res = RequestControl(in_data).http_request()
TearDownHandler(res).teardown_handle()
Assert(in_data['assert_data']).assert_equality(response_data=res.response_data,
sql_data=res.sql_data, status_code=res.status_code)
if __name__ == '__main__':
pytest.main(['test_upload_file.py', '-s', '-W', 'ignore:Module already imported:pytest.PytestWarning'])

View File

@ -0,0 +1,38 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2023-03-10 11:03:09
import allure
import pytest
from utils.read_files_tools.get_yaml_data_analysis import GetTestCase
from utils.assertion.assert_control import Assert
from utils.requests_tool.request_control import RequestControl
from utils.read_files_tools.regular_control import regular
from utils.requests_tool.teardown_control import TearDownHandler
import pytest_rerunfailures
case_id = ['platform_index_01', 'platform_property_02', 'platform_goods_list_03', 'platform_goods_comment_04', 'platform_goods_total_05']
TestData = GetTestCase.case_data(case_id)
re_data = regular(str(TestData))
@allure.epic("JekunAuto小程序接口")
@allure.feature("商城模块")
class TestPlatformIndex:
@allure.story("商城模块")
@pytest.mark.flaky(reruns=3, reruns_delay=3)
@pytest.mark.parametrize('in_data', eval(re_data), ids=[i['detail'] for i in TestData])
def test_platform_index(self, in_data, case_skip):
"""
:param :
:return:
"""
res = RequestControl(in_data).http_request()
TearDownHandler(res).teardown_handle()
Assert(in_data['assert_data']).assert_equality(response_data=res.response_data,
sql_data=res.sql_data, status_code=res.status_code)
if __name__ == '__main__':
pytest.main(['test_platform_index.py', '-s', '-W', 'ignore:Module already imported:pytest.PytestWarning'])

View File

@ -1,6 +1,6 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2023-02-15 17:45:07
# @Time : 2023-03-10 11:03:09
import allure
@ -20,7 +20,8 @@ re_data = regular(str(TestData))
@allure.epic("JekunAuto小程序接口")
@allure.feature("登录模块")
class TestLogin:
@pytest.mask.flaky(reruns=3, reruns_delay=3)
@allure.story("登录")
@pytest.mark.parametrize('in_data', eval(re_data), ids=[i['detail'] for i in TestData])
def test_login(self, in_data, case_skip):

View File

@ -1,6 +1,6 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2023-02-15 17:45:07
# @Time : 2023-03-10 11:03:09
import allure
@ -20,7 +20,8 @@ re_data = regular(str(TestData))
@allure.epic("JekunAuto小程序接口")
@allure.feature("登录模块")
class TestUserInfo:
@pytest.mask.flaky(reruns=3, reruns_delay=3)
@allure.story("用户信息")
@pytest.mark.parametrize('in_data', eval(re_data), ids=[i['detail'] for i in TestData])
def test_user_info(self, in_data, case_skip):

View File

@ -23,6 +23,7 @@ class Assert:
# literal_eval()函数会判断需要计算的内容计算后是不是合法的python类型如果是则进行计算否则不计算就是去''
self.assert_data = ast.literal_eval(cache_regular(str(assert_data)))
# 返回一个字典,{Key:value} value的类型是一个函数或者是一个@staticmethod的静态方法
# {'equals': <function equals at 0x000001F226474670>,'less_than': <function less_than at 0x000001F2264772E0>, 'less_than_or_equals': <function ......'endswith': <function endswith at 0x000001F226477AC0>}
self.functions_mapping = load_module_functions(assert_type)
@staticmethod
@ -115,6 +116,8 @@ class Assert:
resp_data=resp_data,
message=message
)
if len(resp_data) > 1:
str(resp_data)
# 判断assertType为空的情况下则走响应断言
elif assert_types is None:
@ -143,13 +146,10 @@ class Assert:
assert status_code == values
else:
assert_value = self.assert_data[key]['value'] # 获取 yaml 文件中的期望value值
print("---------yaml的断言数据assert_value:{}----------------".format(assert_value))
assert_jsonpath = self.assert_data[key]['jsonpath'] # 获取到 yaml断言中的jsonpath的数据
assert_types = self.assert_data[key]['AssertType']
print("-----------assert_types:{}----------------".format(assert_types))
# 从yaml获取jsonpath拿到对象的接口响应数据
resp_data = jsonpath(json.loads(response_data), assert_jsonpath)
print("-----------响应数据resp_data:{}----------------".format(resp_data))
message = self._message(value=values)
# jsonpath 如果数据获取失败会返回False判断获取成功才会执行如下代码
if resp_data is not False:

View File

@ -110,8 +110,8 @@ def length_less_than_or_equals(
def contains(check_value: Any, expect_value: Any, message: Text = ""):
"""判断期望结果内容包含在实际结果中"""
assert isinstance(
check_value, (list, tuple, dict, str, bytes)
), "expect_value 需要为 list/tuple/dict/str/bytes 类型"
check_value, (list, tuple, dict, str, bytes, Text)
), "expect_value 需要为 list/tuple/dict/str/bytes Text 类型"
assert expect_value in check_value, message

View File

@ -4,6 +4,7 @@ from typing import Text, Dict, Callable, Union, Optional, List, Any
from dataclasses import dataclass
from pydantic import BaseModel, Field
class NotificationType(Enum):
""" 自动化通知方式 """
DEFAULT = 0
@ -13,6 +14,9 @@ class NotificationType(Enum):
FEI_SHU = 4
# dataclass 提供一个简便的方式创建数据类, 默认实现__init__(), __repr__(), __eq__()方法.
# dataclass支持数据类型的嵌套
# 支持将数据设置为不可变
@dataclass
class TestMetrics:
""" 用例执行数据 """
@ -36,12 +40,14 @@ class RequestType(Enum):
EXPORT = "EXPORT"
NONE = "NONE"
# TODO 为什么判断下是否有方法
def load_module_functions(module) -> Dict[Text, Callable]:
""" 获取 module中方法的名称和所在的内存地址
原理把字典对象转成可遍历对象判断里面是否有fuction再组装成字典返回
vars()内置函数参数一个字典对象返回一个字典
itemns()操作字典函数字典不可遍历items返回一个可遍历的对面dict_items([('name', 'jack'), ('item', '1')])
module_functions:{'equal':<function equals at 0X000***>'less_than':<function less_than at 0x00**>}
"""
module_functions = {}
@ -242,3 +248,8 @@ class AssertMethod(Enum):
contained_by = 'contained_by'
startswith = 'startswith'
endswith = 'endswith'
class Reruns(BaseModel):
reruns: int
reruns_delay: int

View File

@ -254,7 +254,7 @@ class CaseData:
self.raise_value_null_error(case_id=case_id, data_name="dependence_case")
) from exc
# TODO 对 dependence_case_data 中的值进行验证
# 对 dependence_case_data 中的值进行验证
def get_dependence_case_data(
self,
case_id: Text,

View File

@ -40,7 +40,8 @@ def write_testcase_file(*, allure_epic, allure_feature, class_title,
now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
# 设置为True的时候修改yaml文件的用例代码中的内容会实时更新
real_time_update_test_cases = conf_data['real_time_update_test_cases']
reruns = conf_data['reruns']
reruns_delay = conf_data['reruns_delay']
page = f'''#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : {now}
@ -63,7 +64,8 @@ re_data = regular(str(TestData))
@allure.epic("{allure_epic}")
@allure.feature("{allure_feature}")
class Test{class_title}:
@pytest.mark.flaky(reruns={reruns}, reruns_delay={reruns_delay})
@allure.story("{allure_story}")
@pytest.mark.parametrize('in_data', eval(re_data), ids=[i['detail'] for i in TestData])
def test_{func_title}(self, in_data, case_skip):

View File

@ -15,6 +15,7 @@ import urllib3
from requests_toolbelt import MultipartEncoder
from common.setting import ensure_path_sep
from utils.logging_tool.log_control import ERROR
from utils.other_tools.exceptions import ValueNotFoundError
from utils.other_tools.models import RequestType
from utils.logging_tool.log_decorator import log_decorator
from utils.mysql_tool.mysql_control import AssertExecution
@ -48,8 +49,8 @@ class RequestControl:
_data = self.__yaml_case.data
for key, value in ast.literal_eval(cache_regular(str(_data)))['data'].items():
file_data[key] = value
except KeyError:
...
except KeyError as exc:
raise ValueNotFoundError("{data}数据未找到请检查yaml文件是否添加") from exc
@classmethod
def multipart_data(
@ -58,6 +59,7 @@ class RequestControl:
""" 处理上传文件数据 """
multipart = MultipartEncoder(
fields=file_data, # 字典格式
# boundary参数 可有可无
boundary='-----------------------------' + str(random.randint(int(1e28), int(1e29 - 1)))
)
return multipart
@ -386,7 +388,7 @@ class RequestControl:
allure_step("响应结果: ", res)
@log_decorator(True)
@execution_duration(3000)
@execution_duration(5000)
# @encryption("md5")
def http_request(
self,

View File

@ -9,7 +9,6 @@ import time
from typing import Text
from datetime import datetime
def count_milliseconds():
"""
计算时间
@ -21,7 +20,7 @@ def count_milliseconds():
return access_delta
def timestamp_conversion(time_str: Text) -> int:
def timestamp_conversion(time_str: Text) -> float:
"""
时间戳转换将日期格式转换成时间戳
:param time_str: 时间
@ -30,15 +29,16 @@ def timestamp_conversion(time_str: Text) -> int:
try:
datetime_format = datetime.strptime(str(time_str), "%Y-%m-%d %H:%M:%S")
timestamp = int(
time.mktime(datetime_format.timetuple()) * 1000.0
+ datetime_format.microsecond / 1000.0
)
# timestamp = int(
# time.mktime(datetime_format.timetuple()) * 1000.0
# + datetime_format.microsecond / 1000.0
# )
timestamp = datetime.timestamp(datetime_format)
return timestamp
except ValueError as exc:
raise ValueError('日期格式错误, 需要传入得格式为 "%Y-%m-%d %H:%M:%S" ') from exc
a = 111
def time_conversion(time_num: int):
"""
时间戳转换成日期