addproject

This commit is contained in:
qiaoxinjiu
2026-01-22 19:10:37 +08:00
commit 6994b185a3
184 changed files with 21039 additions and 0 deletions

View File

@@ -0,0 +1,190 @@
# Jenkins + Allure 报告集成配置指南
## 前置要求
1. **Jenkins已安装并运行**
2. **安装Allure插件**
- 进入 Jenkins → Manage Jenkins → Manage Plugins
- 搜索并安装 "Allure Plugin"
3. **安装Allure命令行工具**
- 下载https://github.com/allure-framework/allure2/releases
- 解压并添加到系统PATH环境变量
- 或在Jenkins全局工具配置中配置Allure路径
## Jenkins配置步骤
### 方式一使用Jenkinsfile推荐
1. **在Jenkins中创建Pipeline任务**
- 新建任务 → 选择 "Pipeline"
- 任务名称:例如 "ZZYY_Test_Automation"
2. **配置Pipeline**
- Pipeline definition → Pipeline script from SCM
- SCM: Git或其他版本控制
- Script Path: `zhyy/test_case/Jenkinsfile`
- 保存
3. **运行任务**
- 点击 "Build with Parameters"
- 选择运行方式RUN_TYPE
- 填写相应参数
- 点击 "Build"
### 方式二:自由风格项目配置
1. **创建自由风格项目**
- 新建任务 → 选择 "Freestyle project"
- 任务名称:例如 "ZZYY_Test_Automation"
2. **配置源码管理**
- Source Code Management → Git
- Repository URL: 你的Git仓库地址
- Branch: 分支名称
3. **配置构建步骤**
- Build → Add build step → Execute shellLinux/Mac或 Execute Windows batch commandWindows
- 命令示例:
```bash
# Linux/Mac
cd ${WORKSPACE}
python zhyy/test_case/run_tests.py --all --no-report
```
```batch
# Windows
cd %WORKSPACE%
python zhyy\test_case\run_tests.py --all --no-report
```
4. **配置Allure报告**
- Post-build Actions → Add post-build action → Allure Report
- Results path: `zhyy/test_case/reports/allure-results`
- Report path: `zhyy/test_case/reports/allure-report`(可选)
- 保存
5. **配置参数化构建(可选)**
- This project is parameterized → Add Parameter
- 添加Choice Parameter
- Name: `RUN_TYPE`
- Choices: `all`, `feature`, `story`, `dir`, `file`, `keyword`, `marker`
- 添加String Parameter根据需要
- `FEATURE_NAME`, `STORY_NAME`, `DIR_PATH`, `FILE_PATH`, `KEYWORD`, `MARKER`
6. **修改构建命令以使用参数**
```bash
# Linux/Mac
cd ${WORKSPACE}
if [ "${RUN_TYPE}" = "all" ]; then
python zhyy/test_case/run_tests.py --all --no-report
elif [ "${RUN_TYPE}" = "feature" ]; then
python zhyy/test_case/run_tests.py --feature "${FEATURE_NAME}" --no-report
elif [ "${RUN_TYPE}" = "dir" ]; then
python zhyy/test_case/run_tests.py --dir "${DIR_PATH}" --no-report
# ... 其他条件
fi
```
## Allure插件配置
### 全局工具配置
1. **配置Allure命令行工具路径**
- Manage Jenkins → Global Tool Configuration
- Allure Commandline → Add Allure Commandline
- Name: `Allure`(或自定义名称)
- Installation directory: Allure安装路径`C:\allure\bin` 或 `/usr/local/allure/bin`
- 保存
### 项目配置
1. **在项目配置中添加Allure报告**
- Post-build Actions → Allure Report
- Results path: `zhyy/test_case/reports/allure-results`
- 勾选 "Keep allure results history"
## 环境变量配置
### Jenkins全局环境变量
1. **配置Python路径如需要**
- Manage Jenkins → Configure System → Global properties
- Environment variables → Add
- Name: `PYTHONPATH`
- Value: `${WORKSPACE}`
### 项目环境变量
在Pipeline或构建脚本中设置
```groovy
environment {
PYTHONPATH = "${WORKSPACE}"
ALLURE_RESULTS = "${WORKSPACE}/zhyy/test_case/reports/allure-results"
}
```
## 使用示例
### 运行所有测试
```bash
python zhyy/test_case/run_tests.py --all --no-report
```
### 按目录运行
```bash
python zhyy/test_case/run_tests.py --dir "接口/SZPurchase" --no-report
```
### 按Feature标签运行
```bash
python zhyy/test_case/run_tests.py --feature "深圳采购工作台采购订单页面" --no-report
```
## 报告查看
1. **在Jenkins中查看**
- 构建完成后,在项目页面左侧菜单会出现 "Allure Report" 链接
- 点击即可查看详细的测试报告
2. **报告内容**
- 测试用例执行情况
- 通过/失败统计
- 执行时间
- 测试步骤详情
- 截图和日志(如果配置了)
## 常见问题
### 1. Allure命令未找到
- 确保Allure已安装并添加到PATH
- 或在Jenkins全局工具配置中指定Allure路径
### 2. 模块导入错误
- 检查PYTHONPATH环境变量
- 确保项目根目录在Python路径中
### 3. 报告未生成
- 检查allure-results目录是否存在且包含数据
- 检查Jenkins Allure插件配置的路径是否正确
### 4. 权限问题
- 确保Jenkins有权限访问工作空间目录
- 确保有权限执行Python和Allure命令
## 邮件通知配置(可选)
在Post-build Actions中添加
- Email Notification
- 配置收件人、主题等
- 可以附加Allure报告链接
## 定时构建(可选)
在项目配置中:
- Build Triggers → Build periodically
- 例如:`H 2 * * *`每天凌晨2点执行
## 多节点执行(可选)
如果有多台Jenkins节点
- 在Pipeline中配置 `agent { label 'your-node-label' }`
- 或在自由风格项目中配置 "Restrict where this project can be run"

175
zhyy/test_case/Jenkinsfile vendored Normal file
View File

@@ -0,0 +1,175 @@
pipeline {
agent any
options {
// 保留最近10次构建
buildDiscarder(logRotator(numToKeepStr: '10'))
// 超时时间60分钟
timeout(time: 60, unit: 'MINUTES')
}
environment {
// Python路径
PYTHONPATH = "${WORKSPACE}"
// Allure结果目录
ALLURE_RESULTS = "${WORKSPACE}/zhyy/test_case/reports/allure-results"
// Allure报告目录
ALLURE_REPORT = "${WORKSPACE}/zhyy/test_case/reports/allure-report"
}
stages {
stage('Checkout') {
steps {
echo '检出代码...'
checkout scm
}
}
stage('环境准备') {
steps {
echo '准备测试环境...'
script {
// 确保Python环境
sh '''
python --version
pip --version
'''
}
}
}
stage('运行测试') {
steps {
echo '执行测试用例...'
script {
// 根据参数选择运行方式
def runType = params.RUN_TYPE ?: 'all'
def testCommand = ''
switch(runType) {
case 'all':
testCommand = 'python zhyy/test_case/run_tests.py --all --no-report'
break
case 'feature':
def feature = params.FEATURE_NAME ?: ''
testCommand = "python zhyy/test_case/run_tests.py --feature \"${feature}\" --no-report"
break
case 'story':
def story = params.STORY_NAME ?: ''
testCommand = "python zhyy/test_case/run_tests.py --story \"${story}\" --no-report"
break
case 'dir':
def dir = params.DIR_PATH ?: ''
testCommand = "python zhyy/test_case/run_tests.py --dir \"${dir}\" --no-report"
break
case 'file':
def file = params.FILE_PATH ?: ''
testCommand = "python zhyy/test_case/run_tests.py --file \"${file}\" --no-report"
break
case 'keyword':
def keyword = params.KEYWORD ?: ''
testCommand = "python zhyy/test_case/run_tests.py --keyword \"${keyword}\" --no-report"
break
case 'marker':
def marker = params.MARKER ?: ''
testCommand = "python zhyy/test_case/run_tests.py --marker \"${marker}\" --no-report"
break
default:
testCommand = 'python zhyy/test_case/run_tests.py --all --no-report'
}
sh """
cd ${WORKSPACE}
${testCommand}
"""
}
}
post {
always {
// 无论成功失败都收集测试结果
echo '收集测试结果...'
}
}
}
stage('生成Allure报告') {
steps {
echo '生成Allure报告...'
script {
sh """
cd ${WORKSPACE}
allure generate ${ALLURE_RESULTS} -o ${ALLURE_REPORT} --clean || echo "Allure报告生成失败但继续执行"
"""
}
}
}
}
post {
always {
// 发布Allure报告
allure([
includeProperties: false,
jdk: '',
properties: [],
reportBuildPolicy: 'ALWAYS',
results: [[path: 'zhyy/test_case/reports/allure-results']]
])
// 清理工作空间(可选)
// cleanWs()
}
success {
echo '✓ 测试执行成功'
// 可以在这里添加成功通知,如发送邮件、钉钉等
}
failure {
echo '✗ 测试执行失败'
// 可以在这里添加失败通知
}
unstable {
echo '⚠ 测试执行不稳定'
}
}
}
// 参数化构建
properties([
parameters([
choice(
name: 'RUN_TYPE',
choices: ['all', 'feature', 'story', 'dir', 'file', 'keyword', 'marker'],
description: '选择运行方式'
),
string(
name: 'FEATURE_NAME',
defaultValue: '',
description: 'Feature标签名称当RUN_TYPE=feature时使用'
),
string(
name: 'STORY_NAME',
defaultValue: '',
description: 'Story标签名称当RUN_TYPE=story时使用'
),
string(
name: 'DIR_PATH',
defaultValue: '接口/SZPurchase',
description: '测试目录路径当RUN_TYPE=dir时使用相对于TestCase目录'
),
string(
name: 'FILE_PATH',
defaultValue: '接口/SZPurchase/PurchaseOrderManage.py',
description: '测试文件路径当RUN_TYPE=file时使用相对于TestCase目录'
),
string(
name: 'KEYWORD',
defaultValue: 'purchase',
description: '关键字当RUN_TYPE=keyword时使用'
),
string(
name: 'MARKER',
defaultValue: 'smoke',
description: 'Pytest标记当RUN_TYPE=marker时使用'
)
])
])

View File

@@ -0,0 +1,84 @@
# Jenkins + Allure 集成快速参考
## 快速开始
### 1. 安装Allure插件
- Jenkins → Manage Jenkins → Manage Plugins
- 搜索 "Allure Plugin" 并安装
### 2. 配置Allure工具
- Manage Jenkins → Global Tool Configuration
- Allure Commandline → 添加Allure安装路径
### 3. 创建Jenkins任务
#### 方式A使用Jenkinsfile推荐
1. 新建Pipeline任务
2. Pipeline script from SCM
3. Script Path: `zhyy/test_case/Jenkinsfile`
4. 保存并运行
#### 方式B自由风格项目
1. 新建Freestyle project
2. 构建步骤:执行 `jenkins_build.sh``jenkins_build.bat`
3. Post-build Actions → Allure Report
4. Results path: `zhyy/test_case/reports/allure-results`
## 参数化构建
在Jenkins任务中配置以下参数
| 参数名 | 类型 | 说明 | 示例值 |
|--------|------|------|--------|
| RUN_TYPE | Choice | 运行方式 | all, feature, story, dir, file, keyword, marker |
| FEATURE_NAME | String | Feature标签 | 深圳采购工作台采购订单页面 |
| STORY_NAME | String | Story标签 | 验证采购工作台采购订单页面列表查询 |
| DIR_PATH | String | 目录路径 | 接口/SZPurchase |
| FILE_PATH | String | 文件路径 | 接口/SZPurchase/PurchaseOrderManage.py |
| KEYWORD | String | 关键字 | purchase |
| MARKER | String | Pytest标记 | smoke |
## 常用命令
### 本地运行
```bash
# 运行所有测试
python run_tests.py
# 按目录运行
python run_tests.py --dir "接口/SZPurchase"
# 按文件运行
python run_tests.py --file "接口/SZPurchase/PurchaseOrderManage.py"
# 生成并打开报告
python run_tests.py --all --report --open
```
### Jenkins中运行
```bash
# 使用构建脚本(自动检测参数)
bash jenkins_build.sh
# 或直接使用run_tests.py
python run_tests.py --all --no-report
```
## Allure报告路径
- **结果目录**: `zhyy/test_case/reports/allure-results`
- **报告目录**: `zhyy/test_case/reports/allure-report`
- **Jenkins中查看**: 构建完成后点击左侧 "Allure Report" 链接
## 环境变量
Jenkins会自动设置以下环境变量
- `WORKSPACE`: Jenkins工作空间路径
- `BUILD_NUMBER`: 构建编号
- `JENKINS_URL`: Jenkins服务器地址
`run_tests.py` 会自动检测Jenkins环境并调整路径。
## 详细文档
更多配置说明请参考:`JENKINS_SETUP.md`

View File

@@ -0,0 +1,96 @@
# 测试执行说明
## 统一测试执行工具
使用 `run_tests.py` 可以方便地执行各种测试用例。
## 安装依赖
```bash
pip install pytest
pip install pytest-allure-adaptor # 或 allure-pytest
pip install allure-python-commons
```
## 使用方法
### 1. 运行所有测试用例
```bash
python run_tests.py --all
```
### 2. 按 Allure Feature 标签运行
```bash
python run_tests.py --feature "深圳采购工作台采购订单页面"
```
### 3. 按 Allure Story 标签运行
```bash
python run_tests.py --story "验证采购工作台采购订单页面列表查询"
```
### 4. 按目录运行
```bash
python run_tests.py --dir "接口/SZPurchase"
```
### 5. 按文件运行
```bash
python run_tests.py --file "接口/SZPurchase/PurchaseOrderManage.py"
```
### 6. 按关键字运行(匹配文件名或类名)
```bash
python run_tests.py --keyword "purchase"
```
### 7. 按 pytest 标记运行
```bash
python run_tests.py --marker "smoke"
```
### 8. 生成并打开 Allure 报告
```bash
python run_tests.py --all --report --open
```
## 快捷方式Windows
- `run_all.bat` - 运行所有测试用例
- `run_purchase.bat` - 运行采购相关测试用例
## 参数说明
- `--all`: 运行所有测试用例
- `--feature <name>`: 按allure feature标签运行
- `--story <name>`: 按allure story标签运行
- `--dir <path>`: 按目录运行相对于TestCase目录
- `--file <path>`: 按文件运行相对于TestCase目录
- `--keyword <keyword>`: 按关键字运行
- `--marker <marker>`: 按pytest标记运行
- `--report`: 生成Allure报告
- `--open`: 打开Allure报告
- `--no-report`: 不生成Allure报告
## 报告位置
- Allure结果: `reports/allure-results/`
- Allure报告: `reports/allure-report/`
## 查看报告
生成报告后,可以使用以下命令打开:
```bash
allure open reports/allure-report
```
或者直接使用 `--open` 参数自动打开。

View File

@@ -0,0 +1,9 @@
*** Settings ***
*** Variables ***
${parent_phone_1} 13400234900 # 增长业务使用(陈江)(陈洁-login、留资等也在使用),逻辑思维用户
${parentphone_customerId_1} 6022462 #用户 13400234900逻辑思维线索id
${parent_phone_2} 12030990019 #张楠 course_package.robot(陈洁-login、留资等也在使用),中文素养用户
${parent_phone_3} 13400234902 # 海报任务专用

View File

View File

@@ -0,0 +1,75 @@
# -*- coding: utf-8 -*-
import allure
import logging
from zhyy.library.BusinessKw.SZPurchase.PurchaseOrderManage import PurchaseOrder
@allure.feature('深圳采购工作台采购订单页面')
class Test_purchase_order(object):
test_case = PurchaseOrder()
def teardown_method(self):
logging.info("-----------------------------End-------------------------------")
@allure.story("验证采购工作台采购订单页面列表查询")
def test_check_purchase_order_page(self):
purchase_order_code = 'PO251209048' # 采购单号 必填
supplier_company_ids = ['334'] # 供应商id 非必填
payment_status = '0' # 付款状态 非必填
status = '0' # 采购单状态 非必填
page_no = 1 # 页码 必填
page_size = 10 # 每页条数 必填
response_data = self.test_case.kw_zhyy_get_purchase_page_post(
note="采购工作台采购订单页面列表查询",
user='purchase',
order_sn=purchase_order_code,
supplier_company_ids=supplier_company_ids,
payment_status=payment_status,
status=status,
page_no=page_no,
page_size=page_size
)
# 断言检查
assert response_data is not None, "响应数据不能为空"
assert 'code' in response_data, "响应数据中缺少code字段"
assert response_data['code'] == 0, "接口调用失败code: {}, msg: {}".format(
response_data.get('code'), response_data.get('msg', '未知错误'))
assert 'data' in response_data, "响应数据中缺少data字段"
assert response_data['data'] is not None, "响应数据中的data字段不能为空"
# 如果传入了采购单号,检查返回的数据中是否包含该采购单号
if purchase_order_code:
data = response_data.get('data', {})
order_found = False
# 检查返回的数据结构,可能包含列表或其他结构
if isinstance(data, dict):
# 如果data是字典可能包含records、list、data等字段
records = data.get('records') or data.get('list') or data.get('data') or []
if isinstance(records, list) and len(records) > 0:
# 检查列表中是否包含指定的采购单号
for item in records:
if isinstance(item, dict):
order_sn = item.get('order_sn') or item.get('orderSn') or item.get('orderSn')
if order_sn == purchase_order_code:
order_found = True
break
elif isinstance(data, list):
# 如果data本身就是列表
for item in data:
if isinstance(item, dict):
order_sn = item.get('order_sn') or item.get('orderSn') or item.get('orderSn')
if order_sn == purchase_order_code:
order_found = True
break
if order_found:
logging.info("✓ 断言通过:返回的数据中包含采购单号 {}".format(purchase_order_code))
else:
logging.warning("⚠ 警告:返回的数据中未找到采购单号: {},但接口调用成功".format(purchase_order_code))
logging.info("✓ 所有断言检查通过")
print("✓ 查询成功,响应数据: {}".format(response_data))

View File

@@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
import allure
import logging
from zhyy.library.BusinessKw.SZPurchase.index import PurchaseIndex
@allure.feature('深圳采购工作台首页')
class Test_purchase_index(object):
# config = ReadConfig.ReadConfig() # 调用读取配置文件的方法类
test_case = PurchaseIndex()
def teardown_method(self):
logging.info("-----------------------------End-------------------------------")
@allure.story("验证采购工作台待办任务与在办任务功能")
def test_check_todo(self):
get_purchase_data = self.test_case.kw_zhyy_get_todo(note="采购工作台首页待办任务PO与在办任务PO", user='purchase')

View File

@@ -0,0 +1,14 @@
# -*- coding: utf-8 -*-
"""
测试配置文件
用于设置测试环境的路径和配置
"""
import os
import sys
# 添加项目根目录到 Python 路径,确保能导入 zhyy 模块
current_file_path = os.path.abspath(__file__)
# 从 test_case/TestCase/接口/conftest.py 向上找到项目根目录 d:\zhyy
project_root = os.path.abspath(os.path.join(os.path.dirname(current_file_path), '../../../..'))
if project_root not in sys.path:
sys.path.insert(0, project_root)

View File

Binary file not shown.

View File

@@ -0,0 +1,44 @@
@echo off
REM Jenkins构建脚本Windows
REM 用于在Jenkins中执行测试并生成Allure报告
setlocal enabledelayedexpansion
REM 设置工作目录
if defined WORKSPACE (
cd /d %WORKSPACE%
) else (
cd /d %~dp0..
)
REM 进入测试目录
cd zhyy\test_case
REM 运行测试(根据参数选择运行方式)
if "%RUN_TYPE%"=="" set RUN_TYPE=all
if "%RUN_TYPE%"=="all" (
python run_tests.py --all --no-report
) else if "%RUN_TYPE%"=="feature" (
python run_tests.py --feature "%FEATURE_NAME%" --no-report
) else if "%RUN_TYPE%"=="story" (
python run_tests.py --story "%STORY_NAME%" --no-report
) else if "%RUN_TYPE%"=="dir" (
python run_tests.py --dir "%DIR_PATH%" --no-report
) else if "%RUN_TYPE%"=="file" (
python run_tests.py --file "%FILE_PATH%" --no-report
) else if "%RUN_TYPE%"=="keyword" (
python run_tests.py --keyword "%KEYWORD%" --no-report
) else if "%RUN_TYPE%"=="marker" (
python run_tests.py --marker "%MARKER%" --no-report
) else (
echo 未知的运行类型: %RUN_TYPE%
exit /b 1
)
REM 生成Allure报告Jenkins插件会自动处理这里可选
REM allure generate reports\allure-results -o reports\allure-report --clean
echo 测试执行完成Allure结果已保存到: reports\allure-results
endlocal

View File

@@ -0,0 +1,47 @@
#!/bin/bash
# Jenkins构建脚本Linux/Mac
# 用于在Jenkins中执行测试并生成Allure报告
set -e # 遇到错误立即退出
# 设置工作目录
cd ${WORKSPACE:-$(pwd)}
# 进入测试目录
cd zhyy/test_case
# 运行测试(根据参数选择运行方式)
RUN_TYPE=${RUN_TYPE:-all}
case ${RUN_TYPE} in
all)
python run_tests.py --all --no-report
;;
feature)
python run_tests.py --feature "${FEATURE_NAME}" --no-report
;;
story)
python run_tests.py --story "${STORY_NAME}" --no-report
;;
dir)
python run_tests.py --dir "${DIR_PATH}" --no-report
;;
file)
python run_tests.py --file "${FILE_PATH}" --no-report
;;
keyword)
python run_tests.py --keyword "${KEYWORD}" --no-report
;;
marker)
python run_tests.py --marker "${MARKER}" --no-report
;;
*)
echo "未知的运行类型: ${RUN_TYPE}"
exit 1
;;
esac
# 生成Allure报告Jenkins插件会自动处理这里可选
# allure generate reports/allure-results -o reports/allure-report --clean || true
echo "测试执行完成Allure结果已保存到: reports/allure-results"

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,4 @@
"Epic","Feature","Story","FAILED","BROKEN","PASSED","SKIPPED","UNKNOWN"
"","深圳采购工作台首页","验证采购工作台待办任务与在办任务功能","0","0","2","0","0"
"","深圳采购工作台采购订单页面","验证采购工作台采购订单页面列表查询","0","0","2","0","0"
"","","","0","0","1","0","0"
1 Epic Feature Story FAILED BROKEN PASSED SKIPPED UNKNOWN
2 深圳采购工作台首页 验证采购工作台待办任务与在办任务功能 0 0 2 0 0
3 深圳采购工作台采购订单页面 验证采购工作台采购订单页面列表查询 0 0 2 0 0
4 0 0 1 0 0

View File

@@ -0,0 +1,6 @@
"Status","Start Time","Stop Time","Duration in ms","Parent Suite","Suite","Sub Suite","Test Class","Test Method","Name","Description"
"passed","Thu Jan 22 17:45:22 CST 2026","Thu Jan 22 17:45:24 CST 2026","2362","zhyy.test_case.TestCase.接口.SZPurchase","index","Test_purchase_index","","","test_check_todo",""
"passed","Thu Jan 22 18:16:40 CST 2026","Thu Jan 22 18:16:41 CST 2026","528","zhyy.test_case.TestCase.接口.SZPurchase","PurchaseOrderManage","Test_purchase_order","","","test_check_purchase_order_page",""
"passed","Thu Jan 22 16:47:07 CST 2026","Thu Jan 22 16:47:07 CST 2026","0","zhyy.test_case.TestCase","test_sample","","","","test_sample",""
"passed","Thu Jan 22 18:16:38 CST 2026","Thu Jan 22 18:16:40 CST 2026","2656","zhyy.test_case.TestCase.接口.SZPurchase","index","Test_purchase_index","","","test_check_todo",""
"passed","Thu Jan 22 17:45:24 CST 2026","Thu Jan 22 17:45:25 CST 2026","530","zhyy.test_case.TestCase.接口.SZPurchase","PurchaseOrderManage","Test_purchase_order","","","test_check_purchase_order_page",""
1 Status Start Time Stop Time Duration in ms Parent Suite Suite Sub Suite Test Class Test Method Name Description
2 passed Thu Jan 22 17:45:22 CST 2026 Thu Jan 22 17:45:24 CST 2026 2362 zhyy.test_case.TestCase.接口.SZPurchase index Test_purchase_index test_check_todo
3 passed Thu Jan 22 18:16:40 CST 2026 Thu Jan 22 18:16:41 CST 2026 528 zhyy.test_case.TestCase.接口.SZPurchase PurchaseOrderManage Test_purchase_order test_check_purchase_order_page
4 passed Thu Jan 22 16:47:07 CST 2026 Thu Jan 22 16:47:07 CST 2026 0 zhyy.test_case.TestCase test_sample test_sample
5 passed Thu Jan 22 18:16:38 CST 2026 Thu Jan 22 18:16:40 CST 2026 2656 zhyy.test_case.TestCase.接口.SZPurchase index Test_purchase_index test_check_todo
6 passed Thu Jan 22 17:45:24 CST 2026 Thu Jan 22 17:45:25 CST 2026 530 zhyy.test_case.TestCase.接口.SZPurchase PurchaseOrderManage Test_purchase_order test_check_purchase_order_page

View File

@@ -0,0 +1 @@
module.exports = __webpack_public_path__ + "favicon.ico";

View File

@@ -0,0 +1,150 @@
'use strict';
allure.api.addTranslation('en', {
tab: {
behaviors: {
name: 'Behaviors'
}
},
widget: {
behaviors: {
name: 'Features by stories',
showAll: 'show all'
}
}
});
allure.api.addTranslation('ru', {
tab: {
behaviors: {
name: 'Функциональность'
}
},
widget: {
behaviors: {
name: 'Функциональность',
showAll: 'показать все'
}
}
});
allure.api.addTranslation('zh', {
tab: {
behaviors: {
name: '功能'
}
},
widget: {
behaviors: {
name: '特性场景',
showAll: '显示所有'
}
}
});
allure.api.addTranslation('de', {
tab: {
behaviors: {
name: 'Verhalten'
}
},
widget: {
behaviors: {
name: 'Features nach Stories',
showAll: 'Zeige alle'
}
}
});
allure.api.addTranslation('he', {
tab: {
behaviors: {
name: 'התנהגויות'
}
},
widget: {
behaviors: {
name: 'תכונות לפי סיפורי משתמש',
showAll: 'הצג הכול'
}
}
});
allure.api.addTranslation('br', {
tab: {
behaviors: {
name: 'Comportamentos'
}
},
widget: {
behaviors: {
name: 'Funcionalidades por história',
showAll: 'Mostrar tudo'
}
}
});
allure.api.addTranslation('ja', {
tab: {
behaviors: {
name: '振る舞い'
}
},
widget: {
behaviors: {
name: 'ストーリー別の機能',
showAll: '全て表示'
}
}
});
allure.api.addTranslation('es', {
tab: {
behaviors: {
name: 'Funcionalidades'
}
},
widget: {
behaviors: {
name: 'Funcionalidades por Historias de Usuario',
showAll: 'mostrar todo'
}
}
});
allure.api.addTranslation('kr', {
tab: {
behaviors: {
name: '동작'
}
},
widget: {
behaviors: {
name: '스토리별 기능',
showAll: '전체 보기'
}
}
});
allure.api.addTab('behaviors', {
title: 'tab.behaviors.name', icon: 'fa fa-list',
route: 'behaviors(/)(:testGroup)(/)(:testResult)(/)(:testResultTab)(/)',
onEnter: (function (testGroup, testResult, testResultTab) {
return new allure.components.TreeLayout({
testGroup: testGroup,
testResult: testResult,
testResultTab: testResultTab,
tabName: 'tab.behaviors.name',
baseUrl: 'behaviors',
url: 'data/behaviors.json',
csvUrl: 'data/behaviors.csv'
});
})
});
allure.api.addWidget('widgets', 'behaviors', allure.components.WidgetStatusView.extend({
rowTag: 'a',
title: 'widget.behaviors.name',
baseUrl: 'behaviors',
showLinks: true
}));

View File

@@ -0,0 +1,88 @@
'use strict';
allure.api.addTranslation('en', {
tab: {
packages: {
name: 'Packages'
}
}
});
allure.api.addTranslation('ru', {
tab: {
packages: {
name: 'Пакеты'
}
}
});
allure.api.addTranslation('zh', {
tab: {
packages: {
name: '包'
}
}
});
allure.api.addTranslation('de', {
tab: {
packages: {
name: 'Pakete'
}
}
});
allure.api.addTranslation('he', {
tab: {
packages: {
name: 'חבילות'
}
}
});
allure.api.addTranslation('br', {
tab: {
packages: {
name: 'Pacotes'
}
}
});
allure.api.addTranslation('ja', {
tab: {
packages: {
name: 'パッケージ'
}
}
});
allure.api.addTranslation('es', {
tab: {
packages: {
name: 'Paquetes'
}
}
});
allure.api.addTranslation('kr', {
tab: {
packages: {
name: '패키지'
}
}
});
allure.api.addTab('packages', {
title: 'tab.packages.name', icon: 'fa fa-align-left',
route: 'packages(/)(:testGroup)(/)(:testResult)(/)(:testResultTab)(/)',
onEnter: (function (testGroup, testResult, testResultTab) {
return new allure.components.TreeLayout({
testGroup: testGroup,
testResult: testResult,
testResultTab: testResultTab,
tabName: 'tab.packages.name',
baseUrl: 'packages',
url: 'data/packages.json'
});
})
});

View File

@@ -0,0 +1,97 @@
(function () {
var settings = allure.getPluginSettings('screen-diff', {diffType: 'diff'});
function renderImage(src) {
return '<div class="screen-diff__container">' +
'<img class="screen-diff__image" src="data/attachments/' + src + '">' +
'</div>';
}
function renderDiffContent(type, data) {
function findImage(name) {
if (data.testStage && data.testStage.attachments) {
return data.testStage.attachments.filter(function (attachment) {
return attachment.name === name;
})[0];
}
return null;
}
var diffImage = findImage('diff');
var actualImage = findImage('actual');
var expectedImage = findImage('expected');
if (!diffImage && !actualImage && !expectedImage) {
return '<span>Diff, actual and expected image have not been provided.</span>';
}
if (type === 'diff') {
if (!diffImage) {
return renderImage(actualImage.source);
}
return renderImage(diffImage.source);
}
if (type === 'overlay') {
return '<div class="screen-diff__overlay screen-diff__container">' +
'<img class="screen-diff__image" src="data/attachments/' + expectedImage.source + '">' +
'<div class="screen-diff__image-over">' +
'<img class="screen-diff__image" src="data/attachments/' + actualImage.source + '">' +
'</div>' +
'</div>';
}
}
var ScreenDiffView = Backbone.Marionette.View.extend({
className: 'pane__section',
events: {
'click [name="screen-diff-type"]': 'onDiffTypeChange',
'mousemove .screen-diff__overlay': 'onOverlayMove'
},
templateContext: function () {
return {
diffType: settings.get('diffType')
}
},
template: function (data) {
var testType = data.labels.filter(function (label) {
return label.name === 'testType'
})[0];
if (!testType || testType.value !== 'screenshotDiff') {
return '';
}
return '<h3 class="pane__section-title">Screen Diff</h3>' +
'<div class="screen-diff__content">' +
'<div class="screen-diff__switchers">' +
'<label><input type="radio" name="screen-diff-type" value="diff"> Show diff</label>' +
'<label><input type="radio" name="screen-diff-type" value="overlay"> Show overlay</label>' +
'</div>' +
renderDiffContent(data.diffType, data) +
'</div>';
},
adjustImageSize: function (event) {
var overImage = this.$(event.target);
overImage.width(overImage.width());
},
onRender: function () {
const diffType = settings.get('diffType');
this.$('[name="screen-diff-type"][value="' + diffType + '"]').prop('checked', true);
if (diffType === 'overlay') {
this.$('.screen-diff__image-over img').on('load', this.adjustImageSize.bind(this));
}
},
onOverlayMove: function (event) {
var pageX = event.pageX;
var containerScroll = this.$('.screen-diff__container').scrollLeft();
var elementX = event.currentTarget.getBoundingClientRect().left;
var delta = pageX - elementX + containerScroll;
this.$('.screen-diff__image-over').width(delta);
},
onDiffTypeChange: function (event) {
settings.save('diffType', event.target.value);
this.render();
}
});
allure.api.addTestResultBlock(ScreenDiffView, {position: 'before'});
})();

View File

@@ -0,0 +1,26 @@
.screen-diff__switchers {
margin-bottom: 1em;
}
.screen-diff__switchers label + label {
margin-left: 1em;
}
.screen-diff__overlay {
position: relative;
cursor: col-resize;
}
.screen-diff__container {
overflow-x: auto;
}
.screen-diff__image-over {
top: 0;
left: 0;
bottom: 0;
background: #fff;
position: absolute;
overflow: hidden;
box-shadow: 2px 0 1px -1px #aaa;
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,7 @@
@echo off
chcp 65001 >nul
echo ========================================
echo 运行所有测试用例
echo ========================================
python run_tests.py --all --report
pause

View File

@@ -0,0 +1,7 @@
@echo off
chcp 65001 >nul
echo ========================================
echo 运行采购相关测试用例
echo ========================================
python run_tests.py --dir "接口/SZPurchase" --report
pause

328
zhyy/test_case/run_tests.py Normal file
View File

@@ -0,0 +1,328 @@
# -*- coding: utf-8 -*-
"""
统一测试执行文件
支持批量运行所有用例、按标签运行、按目录运行等
"""
import os
import sys
import subprocess
import argparse
from pathlib import Path
# 添加项目根目录到 Python 路径
current_file_path = os.path.abspath(__file__)
project_root = os.path.abspath(os.path.join(os.path.dirname(current_file_path), '../../'))
if project_root not in sys.path:
sys.path.insert(0, project_root)
# 测试目录
TEST_CASE_DIR = 'zhyy/test_case/TestCase'
# 报告目录
REPORT_DIR = os.path.join(os.path.dirname(current_file_path), 'reports')
ALLURE_RESULTS_DIR = os.path.join(REPORT_DIR, 'allure-results')
ALLURE_REPORT_DIR = os.path.join(REPORT_DIR, 'allure-report')
def ensure_dirs():
"""确保报告目录存在"""
os.makedirs(ALLURE_RESULTS_DIR, exist_ok=True)
os.makedirs(ALLURE_REPORT_DIR, exist_ok=True)
def run_pytest(args_list):
"""执行pytest命令"""
# 基础pytest命令
# 设置PYTHONPATH环境变量确保能导入zhyy模块
env = os.environ.copy()
if 'PYTHONPATH' in env:
env['PYTHONPATH'] = project_root + os.pathsep + env['PYTHONPATH']
else:
env['PYTHONPATH'] = project_root
# 检测是否在Jenkins环境中
is_jenkins = 'JENKINS_URL' in env or 'BUILD_NUMBER' in env
if is_jenkins:
print("检测到Jenkins环境使用Jenkins配置...")
# 在Jenkins中报告路径使用绝对路径
if 'WORKSPACE' in env:
workspace = env['WORKSPACE']
# 确保使用Jenkins工作空间的报告目录
global ALLURE_RESULTS_DIR, ALLURE_REPORT_DIR
ALLURE_RESULTS_DIR = os.path.join(workspace, 'zhyy', 'test_case', 'reports', 'allure-results')
ALLURE_REPORT_DIR = os.path.join(workspace, 'zhyy', 'test_case', 'reports', 'allure-report')
ensure_dirs()
cmd = ['python', '-m', 'pytest'] + args_list
print("=" * 80)
print("执行命令: {}".format(' '.join(cmd)))
print("命令列表: {}".format(cmd))
print("测试目录: {}".format(TEST_CASE_DIR))
print("测试目录是否存在: {}".format(os.path.exists(TEST_CASE_DIR)))
if is_jenkins:
print("Jenkins工作空间: {}".format(env.get('WORKSPACE', 'N/A')))
print("构建编号: {}".format(env.get('BUILD_NUMBER', 'N/A')))
print("PYTHONPATH: {}".format(env.get('PYTHONPATH')))
print("Allure结果目录: {}".format(ALLURE_RESULTS_DIR))
print("当前工作目录: {}".format(os.getcwd()))
print("=" * 80)
# 使用 shell=True 来执行命令,确保能正确处理路径和参数
result = subprocess.run(' '.join(cmd), shell=True, cwd=project_root, env=env)
return result.returncode
def find_test_files(directory):
"""递归查找所有测试文件"""
test_files = []
for root, dirs, files in os.walk(directory):
for file in files:
if file.endswith('.py'):
# 检查文件中是否包含测试函数
file_path = os.path.join(root, file)
try:
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
if 'def test_' in content or 'class Test' in content:
test_files.append(file_path)
except:
pass
return test_files
def run_all_tests():
"""运行所有测试用例"""
print("运行所有测试用例...")
# 递归查找所有测试文件
test_files = find_test_files(TEST_CASE_DIR)
if not test_files:
print("未找到测试文件")
return 1
print(f"找到 {len(test_files)} 个测试文件:")
for file in test_files:
print(f" - {file}")
args = [
*test_files,
'-v', # 详细输出
'--tb=short', # 简短的错误信息
f'--alluredir={ALLURE_RESULTS_DIR}', # allure结果目录
]
return run_pytest(args)
def run_by_feature(feature_name):
"""按allure feature标签运行"""
print("按feature标签运行: {}".format(feature_name))
args = [
TEST_CASE_DIR,
'-v',
'--tb=short',
f'--allure-features={feature_name}',
f'--alluredir={ALLURE_RESULTS_DIR}',
]
return run_pytest(args)
def run_by_story(story_name):
"""按allure story标签运行"""
print("按story标签运行: {}".format(story_name))
args = [
TEST_CASE_DIR,
'-v',
'--tb=short',
f'--allure-stories={story_name}',
f'--alluredir={ALLURE_RESULTS_DIR}',
]
return run_pytest(args)
def run_by_marker(marker):
"""按pytest标记运行"""
print("按pytest标记运行: {}".format(marker))
args = [
TEST_CASE_DIR,
'-v',
'--tb=short',
f'-m={marker}',
f'--alluredir={ALLURE_RESULTS_DIR}',
]
return run_pytest(args)
def run_by_dir(directory):
"""按目录运行"""
full_path = os.path.join(TEST_CASE_DIR, directory)
if not os.path.exists(full_path):
print("错误: 目录不存在: {}".format(full_path))
return 1
print("按目录运行: {}".format(directory))
args = [
full_path,
'-v',
'--tb=short',
f'--alluredir={ALLURE_RESULTS_DIR}',
]
return run_pytest(args)
def run_by_file(file_path):
"""按文件运行"""
full_path = os.path.join(TEST_CASE_DIR, file_path)
if not os.path.exists(full_path):
print("错误: 文件不存在: {}".format(full_path))
return 1
print("按文件运行: {}".format(file_path))
args = [
full_path,
'-v',
'--tb=short',
f'--alluredir={ALLURE_RESULTS_DIR}',
]
return run_pytest(args)
def run_by_keyword(keyword):
"""按关键字运行(匹配文件名或类名)"""
print("按关键字运行: {}".format(keyword))
args = [
TEST_CASE_DIR,
'-v',
'--tb=short',
f'-k={keyword}',
f'--alluredir={ALLURE_RESULTS_DIR}',
]
return run_pytest(args)
def generate_allure_report():
"""生成allure报告"""
print("生成Allure报告...")
cmd = [
'allure',
'generate',
ALLURE_RESULTS_DIR,
'-o',
ALLURE_REPORT_DIR,
'--clean'
]
try:
# 使用shell=True来执行命令确保使用正确的环境变量
result = subprocess.run(' '.join(cmd), shell=True, check=True)
print("Allure报告生成成功: {}".format(ALLURE_REPORT_DIR))
print("打开报告命令: allure open {}".format(ALLURE_REPORT_DIR))
return 0
except subprocess.CalledProcessError as e:
print("生成Allure报告失败: {}".format(e))
return 1
except FileNotFoundError:
print("警告: 未找到allure命令请先安装allure")
print("安装方法: https://docs.qameta.io/allure/")
return 1
def open_allure_report():
"""打开allure报告"""
print("打开Allure报告...")
cmd = ['allure', 'open', ALLURE_REPORT_DIR]
try:
# 使用shell=True来执行命令确保使用正确的环境变量
subprocess.Popen(' '.join(cmd), shell=True)
print("Allure报告已在浏览器中打开")
except FileNotFoundError:
print("警告: 未找到allure命令请先安装allure")
def main():
parser = argparse.ArgumentParser(
description='统一测试执行工具',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
使用示例:
# 运行所有测试
python run_tests.py --all
# 按feature标签运行
python run_tests.py --feature "深圳采购工作台采购订单页面"
# 按story标签运行
python run_tests.py --story "验证采购工作台采购订单页面列表查询"
# 按目录运行
python run_tests.py --dir "接口/SZPurchase"
# 按文件运行
python run_tests.py --file "接口/SZPurchase/PurchaseOrderManage.py"
# 按关键字运行
python run_tests.py --keyword "purchase"
# 按pytest标记运行
python run_tests.py --marker "smoke"
# 生成并打开报告
python run_tests.py --all --report --open
"""
)
# 运行选项(不强制要求,默认运行所有测试)
run_group = parser.add_mutually_exclusive_group(required=False)
run_group.add_argument('--all', action='store_true', help='运行所有测试用例(默认)')
run_group.add_argument('--feature', type=str, help='按allure feature标签运行')
run_group.add_argument('--story', type=str, help='按allure story标签运行')
run_group.add_argument('--dir', type=str, help='按目录运行相对于TestCase目录')
run_group.add_argument('--file', type=str, help='按文件运行相对于TestCase目录')
run_group.add_argument('--keyword', type=str, help='按关键字运行(匹配文件名或类名)')
run_group.add_argument('--marker', type=str, help='按pytest标记运行')
# 报告选项
parser.add_argument('--report', action='store_true', help='生成Allure报告')
parser.add_argument('--open', action='store_true', help='打开Allure报告需要先使用--report')
parser.add_argument('--no-report', action='store_true', help='不生成Allure报告默认会生成')
args = parser.parse_args()
# 确保目录存在
ensure_dirs()
# 执行测试(如果没有指定选项,默认运行所有测试)
exit_code = 0
if args.feature:
exit_code = run_by_feature(args.feature)
elif args.story:
exit_code = run_by_story(args.story)
elif args.dir:
exit_code = run_by_dir(args.dir)
elif args.file:
exit_code = run_by_file(args.file)
elif args.keyword:
exit_code = run_by_keyword(args.keyword)
elif args.marker:
exit_code = run_by_marker(args.marker)
else:
# 默认运行所有测试
exit_code = run_all_tests()
# 生成报告
if args.report or (not args.no_report and exit_code == 0):
generate_allure_report()
if args.open:
open_allure_report()
print("=" * 80)
if exit_code == 0:
print("✓ 测试执行完成")
else:
print("✗ 测试执行失败,退出码: {}".format(exit_code))
print("=" * 80)
sys.exit(exit_code)
if __name__ == '__main__':
main()