zhouhui.jiang

添加浏览器自动化测试

Showing 47 changed files with 726 additions and 0 deletions
...@@ -184,3 +184,4 @@ public class DeliveryMainController { ...@@ -184,3 +184,4 @@ public class DeliveryMainController {
184 184
185 185
186 186
187 +
......
...@@ -187,3 +187,4 @@ public class InvoiceMainController { ...@@ -187,3 +187,4 @@ public class InvoiceMainController {
187 187
188 188
189 189
190 +
......
...@@ -63,3 +63,4 @@ public class DeliveryAddReq { ...@@ -63,3 +63,4 @@ public class DeliveryAddReq {
63 63
64 64
65 65
66 +
......
...@@ -39,3 +39,4 @@ public class DeliveryItemAddReq { ...@@ -39,3 +39,4 @@ public class DeliveryItemAddReq {
39 39
40 40
41 41
42 +
......
...@@ -61,3 +61,4 @@ public class DeliveryItemRes { ...@@ -61,3 +61,4 @@ public class DeliveryItemRes {
61 61
62 62
63 63
64 +
......
...@@ -42,3 +42,4 @@ public class DeliveryItemUpdateReq { ...@@ -42,3 +42,4 @@ public class DeliveryItemUpdateReq {
42 42
43 43
44 44
45 +
......
...@@ -69,3 +69,4 @@ public class DeliveryRes { ...@@ -69,3 +69,4 @@ public class DeliveryRes {
69 69
70 70
71 71
72 +
......
...@@ -67,3 +67,4 @@ public class DeliveryUpdateReq { ...@@ -67,3 +67,4 @@ public class DeliveryUpdateReq {
67 67
68 68
69 69
70 +
......
...@@ -44,3 +44,4 @@ public class InvoiceItemAddReq { ...@@ -44,3 +44,4 @@ public class InvoiceItemAddReq {
44 44
45 45
46 46
47 +
......
...@@ -64,3 +64,4 @@ public class InvoiceItemRes { ...@@ -64,3 +64,4 @@ public class InvoiceItemRes {
64 64
65 65
66 66
67 +
......
...@@ -47,3 +47,4 @@ public class InvoiceItemUpdateReq { ...@@ -47,3 +47,4 @@ public class InvoiceItemUpdateReq {
47 47
48 48
49 49
50 +
......
...@@ -57,3 +57,4 @@ public class InvoiceQueryReq { ...@@ -57,3 +57,4 @@ public class InvoiceQueryReq {
57 57
58 58
59 59
60 +
......
...@@ -68,3 +68,4 @@ public class DeliveryItem { ...@@ -68,3 +68,4 @@ public class DeliveryItem {
68 68
69 69
70 70
71 +
......
...@@ -74,3 +74,4 @@ public class InvoiceItem { ...@@ -74,3 +74,4 @@ public class InvoiceItem {
74 74
75 75
76 76
77 +
......
...@@ -83,3 +83,4 @@ public class InvoiceMain { ...@@ -83,3 +83,4 @@ public class InvoiceMain {
83 83
84 84
85 85
86 +
......
...@@ -16,3 +16,4 @@ public interface DeliveryItemMapper extends BaseMapper<DeliveryItem> { ...@@ -16,3 +16,4 @@ public interface DeliveryItemMapper extends BaseMapper<DeliveryItem> {
16 16
17 17
18 18
19 +
......
...@@ -16,3 +16,4 @@ public interface InvoiceItemMapper extends BaseMapper<InvoiceItem> { ...@@ -16,3 +16,4 @@ public interface InvoiceItemMapper extends BaseMapper<InvoiceItem> {
16 16
17 17
18 18
19 +
......
...@@ -16,3 +16,4 @@ public interface InvoiceMainMapper extends BaseMapper<InvoiceMain> { ...@@ -16,3 +16,4 @@ public interface InvoiceMainMapper extends BaseMapper<InvoiceMain> {
16 16
17 17
18 18
19 +
......
...@@ -85,3 +85,4 @@ public interface DeliveryMainService extends IService<DeliveryMain> { ...@@ -85,3 +85,4 @@ public interface DeliveryMainService extends IService<DeliveryMain> {
85 85
86 86
87 87
88 +
......
...@@ -85,3 +85,4 @@ public interface InvoiceMainService extends IService<InvoiceMain> { ...@@ -85,3 +85,4 @@ public interface InvoiceMainService extends IService<InvoiceMain> {
85 85
86 86
87 87
88 +
......
1 +# 浏览器自动化测试
2 +
3 +基于 Python 3.7.8 和 Playwright 的 Apple ERP 系统浏览器自动化测试框架。
4 +
5 +## 环境要求
6 +
7 +- Python 3.7.8+
8 +- Playwright 浏览器自动化框架
9 +
10 +## 安装依赖
11 +
12 +```bash
13 +# 安装 Python 依赖
14 +pip install -r requirements.txt
15 +
16 +# 安装 Playwright 浏览器
17 +playwright install chromium
18 +```
19 +
20 +## 项目结构
21 +
22 +```
23 +tests/browser/
24 +├── __init__.py # 包初始化
25 +├── base_test.py # 基础测试类
26 +├── test_login.py # 登录功能测试
27 +├── test_order_management.py # 订单管理功能测试
28 +├── test_runner.py # 测试运行器
29 +├── requirements.txt # 依赖文件
30 +├── README.md # 说明文档
31 +├── screenshots/ # 测试截图目录
32 +└── reports/ # 测试报告目录
33 +```
34 +
35 +## 使用方法
36 +
37 +### 1. 单独运行测试
38 +
39 +```bash
40 +# 运行登录测试
41 +python test_login.py
42 +
43 +# 运行订单管理测试
44 +python test_order_management.py
45 +```
46 +
47 +### 2. 运行所有测试
48 +
49 +```bash
50 +# 运行所有测试并生成报告
51 +python test_runner.py
52 +```
53 +
54 +### 3. 环境变量配置
55 +
56 +```bash
57 +# 设置前端应用地址(默认: http://localhost:3000)
58 +export FRONTEND_URL=http://localhost:3000
59 +
60 +# 设置浏览器模式(默认: 非无头模式)
61 +export HEADLESS=false
62 +```
63 +
64 +## 测试功能
65 +
66 +### 登录测试 (test_login.py)
67 +
68 +- ✅ 成功登录测试
69 +- ✅ 无效用户名测试
70 +- ✅ 无效密码测试
71 +- ✅ 空凭据测试
72 +- ✅ 记住我功能测试
73 +
74 +### 订单管理测试 (test_order_management.py)
75 +
76 +- ✅ 订单列表显示测试
77 +- ✅ 按订单编号搜索测试
78 +- ✅ 按经销商搜索测试
79 +- ✅ 订单状态筛选测试
80 +- ✅ 重置搜索测试
81 +- ✅ 分页功能测试
82 +- ✅ 表格操作测试
83 +- ✅ 操作按钮测试
84 +
85 +## 测试报告
86 +
87 +测试完成后会自动生成两种格式的报告:
88 +
89 +### HTML 报告
90 +- 美观的可视化界面
91 +- 测试统计信息
92 +- 详细的测试结果
93 +- 截图链接
94 +
95 +### JSON 报告
96 +- 机器可读的格式
97 +- 完整的测试数据
98 +- 便于集成到CI/CD
99 +
100 +## 截图功能
101 +
102 +- 每个测试步骤都会自动截图
103 +- 失败测试会额外截图错误状态
104 +- 截图保存在 `screenshots/` 目录
105 +- 支持全页面截图
106 +
107 +## 配置选项
108 +
109 +### BaseBrowserTest 配置
110 +
111 +```python
112 +# 创建测试实例
113 +test = BaseBrowserTest(
114 + headless=False, # 是否无头模式
115 + slow_mo=500 # 操作间隔时间(毫秒)
116 +)
117 +```
118 +
119 +### 元素定位策略
120 +
121 +基于前端代码分析,使用以下定位策略:
122 +
123 +**登录页面元素:**
124 +- 用户名输入框: `input[placeholder="请输入用户名"]`
125 +- 密码输入框: `input[placeholder="请输入密码"]`
126 +- 登录按钮: `button:has-text("登录")`
127 +- 记住我复选框: `input[type="checkbox"]`
128 +
129 +**订单管理页面元素:**
130 +- 订单编号搜索: `input[placeholder="请输入订单编号"]`
131 +- 经销商编码搜索: `input[placeholder="请输入经销商编码"]`
132 +- 经销商名称搜索: `input[placeholder="请输入经销商名称"]`
133 +- 搜索按钮: `button:has-text("🔍 搜索")`
134 +- 重置按钮: `button:has-text("🔄 重置")`
135 +- 数据表格: `.data-table`
136 +
137 +## 扩展测试
138 +
139 +### 添加新的测试用例
140 +
141 +1. 继承 `BaseBrowserTest`
142 +2. 实现测试方法
143 +3. 使用提供的辅助方法进行元素操作
144 +4. 添加截图和断言
145 +
146 +```python
147 +class MyTest(BaseBrowserTest):
148 + def test_my_feature(self):
149 + # 设置页面
150 + self.setup_my_page()
151 +
152 + # 执行测试操作
153 + self.click_element('button:has-text("我的按钮")')
154 +
155 + # 验证结果
156 + assert self.is_element_visible('.success-message')
157 +
158 + # 截图
159 + self.take_screenshot('my_test_result')
160 +```
161 +
162 +### 自定义测试运行器
163 +
164 +```python
165 +def run_my_tests():
166 + test = MyTest(headless=False, slow_mo=500)
167 +
168 + tests = [
169 + ('我的测试1', test.test_feature_1),
170 + ('我的测试2', test.test_feature_2),
171 + ]
172 +
173 + results = []
174 + for test_name, test_func in tests:
175 + result = test.run_test(test_name, test_func)
176 + results.append(result)
177 +
178 + return results
179 +```
180 +
181 +## 故障排除
182 +
183 +### 常见问题
184 +
185 +1. **浏览器启动失败**
186 + ```bash
187 + # 重新安装浏览器
188 + playwright install chromium
189 + ```
190 +
191 +2. **元素定位失败**
192 + - 检查页面是否完全加载
193 + - 增加等待时间
194 + - 使用更精确的选择器
195 +
196 +3. **截图保存失败**
197 + - 确保 `screenshots/` 目录存在
198 + - 检查文件权限
199 +
200 +4. **测试超时**
201 + - 增加超时时间
202 + - 检查网络连接
203 + - 确认前端服务正常运行
204 +
205 +### 调试技巧
206 +
207 +1. **使用非无头模式**
208 + ```python
209 + test = BaseBrowserTest(headless=False, slow_mo=1000)
210 + ```
211 +
212 +2. **增加等待时间**
213 + ```python
214 + self.page.wait_for_timeout(2000) # 等待2秒
215 + ```
216 +
217 +3. **查看页面内容**
218 + ```python
219 + print(self.page.content()) # 打印页面HTML
220 + ```
221 +
222 +4. **检查元素状态**
223 + ```python
224 + print(self.page.is_visible('selector')) # 检查元素可见性
225 + ```
226 +
227 +## 最佳实践
228 +
229 +1. **测试独立性**: 每个测试都应该独立运行
230 +2. **数据清理**: 测试后清理测试数据
231 +3. **错误处理**: 适当的异常处理和重试机制
232 +4. **截图记录**: 关键步骤都要截图
233 +5. **断言验证**: 使用明确的断言验证结果
234 +6. **性能考虑**: 合理设置等待时间和超时
235 +
236 +## 持续集成
237 +
238 +可以将测试集成到CI/CD流程中:
239 +
240 +```yaml
241 +# GitHub Actions 示例
242 +- name: Run Browser Tests
243 + run: |
244 + cd tests/browser
245 + pip install -r requirements.txt
246 + playwright install chromium
247 + python test_runner.py
248 +```
249 +
250 +## 许可证
251 +
252 +本项目遵循 MIT 许可证。
1 +"""Browser automation tests package (Python 3.7.8 compatible)."""
1 +"""
2 +基础测试类 - 提供通用的浏览器自动化测试功能
3 +Python 3.7.8 兼容
4 +"""
5 +
6 +import os
7 +import time
8 +from datetime import datetime
9 +from typing import Optional, Dict, Any
10 +from playwright.sync_api import sync_playwright, Page, Browser, BrowserContext
11 +
12 +
13 +class BaseBrowserTest:
14 + """浏览器自动化测试基类"""
15 +
16 + def __init__(self, headless: bool = False, slow_mo: int = 100):
17 + """
18 + 初始化浏览器测试
19 +
20 + Args:
21 + headless: 是否无头模式运行
22 + slow_mo: 操作间隔时间(毫秒)
23 + """
24 + self.headless = headless
25 + self.slow_mo = slow_mo
26 + self.playwright = None
27 + self.browser: Optional[Browser] = None
28 + self.context: Optional[BrowserContext] = None
29 + self.page: Optional[Page] = None
30 +
31 + # 测试配置
32 + self.base_url = os.environ.get('FRONTEND_URL', 'http://localhost:3001')
33 + self.screenshots_dir = os.path.join(os.path.dirname(__file__), 'screenshots')
34 + self.reports_dir = os.path.join(os.path.dirname(__file__), 'reports')
35 +
36 + # 创建必要的目录
37 + os.makedirs(self.screenshots_dir, exist_ok=True)
38 + os.makedirs(self.reports_dir, exist_ok=True)
39 +
40 + def setup_browser(self) -> None:
41 + """启动浏览器"""
42 + self.playwright = sync_playwright().start()
43 +
44 + # 启动浏览器(使用Chromium)
45 + self.browser = self.playwright.chromium.launch(
46 + headless=self.headless,
47 + slow_mo=self.slow_mo,
48 + args=['--no-sandbox', '--disable-dev-shm-usage']
49 + )
50 +
51 + # 创建浏览器上下文
52 + self.context = self.browser.new_context(
53 + viewport={'width': 1920, 'height': 1080},
54 + user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
55 + )
56 +
57 + # 创建新页面
58 + self.page = self.context.new_page()
59 +
60 + # 设置默认超时时间
61 + self.page.set_default_timeout(30000) # 30秒
62 +
63 + def teardown_browser(self) -> None:
64 + """关闭浏览器"""
65 + if self.page:
66 + self.page.close()
67 + if self.context:
68 + self.context.close()
69 + if self.browser:
70 + self.browser.close()
71 + if self.playwright:
72 + self.playwright.stop()
73 +
74 + def take_screenshot(self, name: str = None) -> str:
75 + """
76 + 截图
77 +
78 + Args:
79 + name: 截图文件名(不包含扩展名)
80 +
81 + Returns:
82 + 截图文件路径
83 + """
84 + if not name:
85 + timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
86 + name = f'screenshot_{timestamp}'
87 +
88 + screenshot_path = os.path.join(self.screenshots_dir, f'{name}.png')
89 + self.page.screenshot(path=screenshot_path, full_page=True)
90 + print(f"截图已保存: {screenshot_path}")
91 + return screenshot_path
92 +
93 + def wait_for_element(self, selector: str, timeout: int = 10000) -> None:
94 + """
95 + 等待元素出现
96 +
97 + Args:
98 + selector: CSS选择器
99 + timeout: 超时时间(毫秒)
100 + """
101 + self.page.wait_for_selector(selector, timeout=timeout)
102 +
103 + def wait_for_text(self, text: str, timeout: int = 10000) -> None:
104 + """
105 + 等待文本出现
106 +
107 + Args:
108 + text: 要等待的文本
109 + timeout: 超时时间(毫秒)
110 + """
111 + self.page.wait_for_selector(f"text={text}", timeout=timeout)
112 +
113 + def fill_input(self, selector: str, value: str) -> None:
114 + """
115 + 填充输入框
116 +
117 + Args:
118 + selector: CSS选择器
119 + value: 要输入的值
120 + """
121 + self.page.fill(selector, value)
122 +
123 + def click_element(self, selector: str) -> None:
124 + """
125 + 点击元素
126 +
127 + Args:
128 + selector: CSS选择器
129 + """
130 + self.page.click(selector)
131 +
132 + def select_option(self, selector: str, value: str) -> None:
133 + """
134 + 选择下拉框选项
135 +
136 + Args:
137 + selector: CSS选择器
138 + value: 选项值
139 + """
140 + self.page.select_option(selector, value)
141 +
142 + def get_text(self, selector: str) -> str:
143 + """
144 + 获取元素文本
145 +
146 + Args:
147 + selector: CSS选择器
148 +
149 + Returns:
150 + 元素文本内容
151 + """
152 + return self.page.text_content(selector)
153 +
154 + def get_element_count(self, selector: str) -> int:
155 + """
156 + 获取元素数量
157 +
158 + Args:
159 + selector: CSS选择器
160 +
161 + Returns:
162 + 元素数量
163 + """
164 + return self.page.locator(selector).count()
165 +
166 + def is_element_visible(self, selector: str) -> bool:
167 + """
168 + 检查元素是否可见
169 +
170 + Args:
171 + selector: CSS选择器
172 +
173 + Returns:
174 + 元素是否可见
175 + """
176 + return self.page.is_visible(selector)
177 +
178 + def navigate_to(self, url: str) -> None:
179 + """
180 + 导航到指定URL
181 +
182 + Args:
183 + url: 目标URL
184 + """
185 + self.page.goto(url)
186 + self.page.wait_for_load_state('networkidle')
187 +
188 + def login(self, username: str = 'admin', password: str = 'password') -> bool:
189 + """
190 + 登录系统
191 +
192 + Args:
193 + username: 用户名
194 + password: 密码
195 +
196 + Returns:
197 + 登录是否成功
198 + """
199 + try:
200 + # 导航到登录页面
201 + self.navigate_to(self.base_url)
202 +
203 + # 等待登录表单加载
204 + self.wait_for_element('input[placeholder="请输入用户名"]')
205 +
206 + # 填写用户名
207 + self.fill_input('input[placeholder="请输入用户名"]', username)
208 +
209 + # 填写密码
210 + self.fill_input('input[placeholder="请输入密码"]', password)
211 +
212 + # 点击登录按钮
213 + self.click_element('button:has-text("登录")')
214 +
215 + # 等待登录完成(检查是否跳转到首页或出现错误)
216 + try:
217 + # 等待页面跳转或出现成功/失败提示
218 + self.page.wait_for_load_state('networkidle', timeout=10000)
219 +
220 + # 检查是否成功登录(通过URL或页面元素判断)
221 + current_url = self.page.url
222 +
223 + # 等待页面完全加载
224 + self.page.wait_for_timeout(2000)
225 +
226 + # 检查是否有错误提示
227 + error_indicators = [
228 + 'text=用户名或密码错误',
229 + 'text=登录失败',
230 + 'text=Invalid',
231 + 'text=Error',
232 + '.error',
233 + '.alert-danger'
234 + ]
235 +
236 + has_error = False
237 + for indicator in error_indicators:
238 + if self.is_element_visible(indicator):
239 + has_error = True
240 + print(f"发现错误提示: {indicator}")
241 + break
242 +
243 + # 检查是否跳转到其他页面(表示登录成功)
244 + if has_error:
245 + print(f"登录失败,发现错误提示,当前URL: {current_url}")
246 + return False
247 + elif 'dashboard' in current_url or 'main' in current_url:
248 + print(f"登录成功,当前URL: {current_url}")
249 + return True
250 + elif current_url == self.base_url or current_url == f'{self.base_url}/':
251 + print(f"登录失败,仍在登录页面,当前URL: {current_url}")
252 + return False
253 + else:
254 + print(f"登录状态不明确,当前URL: {current_url}")
255 + return False
256 +
257 + except Exception as e:
258 + print(f"登录等待超时: {e}")
259 + return False
260 +
261 + except Exception as e:
262 + print(f"登录过程出错: {e}")
263 + self.take_screenshot('login_error')
264 + return False
265 +
266 + def run_test(self, test_name: str, test_func) -> Dict[str, Any]:
267 + """
268 + 运行测试并生成报告
269 +
270 + Args:
271 + test_name: 测试名称
272 + test_func: 测试函数
273 +
274 + Returns:
275 + 测试结果字典
276 + """
277 + start_time = datetime.now()
278 + result = {
279 + 'test_name': test_name,
280 + 'start_time': start_time.isoformat(),
281 + 'success': False,
282 + 'error': None,
283 + 'screenshots': [],
284 + 'duration': 0
285 + }
286 +
287 + try:
288 + print(f"\n{'='*50}")
289 + print(f"开始测试: {test_name}")
290 + print(f"{'='*50}")
291 +
292 + # 设置浏览器
293 + self.setup_browser()
294 +
295 + # 执行测试
296 + test_func()
297 +
298 + result['success'] = True
299 + print(f"✅ 测试通过: {test_name}")
300 +
301 + except Exception as e:
302 + result['error'] = str(e)
303 + print(f"❌ 测试失败: {test_name}")
304 + print(f"错误信息: {e}")
305 +
306 + # 失败时截图
307 + screenshot_path = self.take_screenshot(f'{test_name}_error')
308 + result['screenshots'].append(screenshot_path)
309 +
310 + finally:
311 + # 清理资源
312 + self.teardown_browser()
313 +
314 + # 计算耗时
315 + end_time = datetime.now()
316 + result['end_time'] = end_time.isoformat()
317 + result['duration'] = (end_time - start_time).total_seconds()
318 +
319 + print(f"测试耗时: {result['duration']:.2f}秒")
320 +
321 + return result
1 +# 浏览器自动化测试依赖
2 +# Python 3.7.8 兼容
3 +
4 +# Playwright 浏览器自动化框架
5 +playwright==1.35.0
6 +
7 +# 其他依赖
8 +requests>=2.25.0
1 +"""
2 +登录功能自动化测试
3 +Python 3.7.8 兼容
4 +"""
5 +
6 +from base_test import BaseBrowserTest
7 +
8 +
9 +class LoginTest(BaseBrowserTest):
10 + """登录测试类"""
11 +
12 + def test_successful_login(self):
13 + """测试成功登录"""
14 + # 测试正常登录
15 + success = self.login('admin', 'password')
16 + assert success, "登录应该成功"
17 +
18 + # 截图验证登录状态
19 + self.take_screenshot('login_success')
20 +
21 + # 验证登录后的页面元素
22 + # 这里可以根据实际登录后的页面结构添加验证
23 + print("登录成功测试通过")
24 +
25 + def test_invalid_username(self):
26 + """测试无效用户名"""
27 + # 测试错误用户名
28 + success = self.login('invalid_user', 'password')
29 + assert not success, "使用无效用户名登录应该失败"
30 +
31 + # 截图验证错误状态
32 + self.take_screenshot('login_invalid_username')
33 +
34 + print("无效用户名测试通过")
35 +
36 + def test_invalid_password(self):
37 + """测试无效密码"""
38 + # 测试错误密码
39 + success = self.login('admin', 'wrong_password')
40 + assert not success, "使用无效密码登录应该失败"
41 +
42 + # 截图验证错误状态
43 + self.take_screenshot('login_invalid_password')
44 +
45 + print("无效密码测试通过")
46 +
47 + def test_empty_credentials(self):
48 + """测试空凭据"""
49 + # 导航到登录页面
50 + self.navigate_to(self.base_url)
51 +
52 + # 等待登录表单
53 + self.wait_for_element('input[placeholder="请输入用户名"]')
54 +
55 + # 不填写任何信息,直接点击登录
56 + self.click_element('button:has-text("登录")')
57 +
58 + # 截图验证错误提示
59 + self.take_screenshot('login_empty_credentials')
60 +
61 + print("空凭据测试通过")
62 +
63 + def test_remember_me(self):
64 + """测试记住我功能"""
65 + # 导航到登录页面
66 + self.navigate_to(self.base_url)
67 +
68 + # 等待登录表单
69 + self.wait_for_element('input[placeholder="请输入用户名"]')
70 +
71 + # 填写用户名和密码
72 + self.fill_input('input[placeholder="请输入用户名"]', 'admin')
73 + self.fill_input('input[placeholder="请输入密码"]', 'password')
74 +
75 + # 勾选记住我
76 + self.click_element('input[type="checkbox"]')
77 +
78 + # 点击登录
79 + self.click_element('button:has-text("登录")')
80 +
81 + # 截图验证
82 + self.take_screenshot('login_remember_me')
83 +
84 + print("记住我功能测试通过")
85 +
86 +
87 +def run_login_tests():
88 + """运行所有登录测试"""
89 + test = LoginTest(headless=False, slow_mo=500) # 非无头模式,慢速执行便于观察
90 +
91 + tests = [
92 + ('成功登录测试', test.test_successful_login),
93 + ('无效用户名测试', test.test_invalid_username),
94 + ('无效密码测试', test.test_invalid_password),
95 + ('空凭据测试', test.test_empty_credentials),
96 + ('记住我功能测试', test.test_remember_me),
97 + ]
98 +
99 + results = []
100 + for test_name, test_func in tests:
101 + result = test.run_test(test_name, test_func)
102 + results.append(result)
103 +
104 + # 打印测试总结
105 + print(f"\n{'='*60}")
106 + print("登录测试总结")
107 + print(f"{'='*60}")
108 +
109 + passed = sum(1 for r in results if r['success'])
110 + total = len(results)
111 +
112 + for result in results:
113 + status = "✅ 通过" if result['success'] else "❌ 失败"
114 + print(f"{result['test_name']}: {status} ({result['duration']:.2f}s)")
115 + if result['error']:
116 + print(f" 错误: {result['error']}")
117 +
118 + print(f"\n总计: {passed}/{total} 个测试通过")
119 +
120 + return results
121 +
122 +
123 +if __name__ == '__main__':
124 + run_login_tests()
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.