test_runner.py
18 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
"""
简化的浏览器自动化测试运行器
Python 3.7.8 兼容
"""
import os
import json
from datetime import datetime
from typing import List, Dict, Any
from test_login import run_login_tests
from test_order_management import run_order_management_tests, OrderManagementTest
from test_delivery_management import run_delivery_management_tests, DeliveryManagementTest
from base_test import BaseBrowserTest
class SimpleTestRunner:
"""简化的测试运行器"""
def __init__(self):
self.reports_dir = os.path.join(os.path.dirname(__file__), 'reports')
self.screenshots_dir = os.path.join(os.path.dirname(__file__), 'screenshots')
# 确保目录存在
os.makedirs(self.reports_dir, exist_ok=True)
os.makedirs(self.screenshots_dir, exist_ok=True)
def generate_html_report(self, all_results: List[Dict[str, Any]]) -> str:
"""生成HTML测试报告"""
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
report_file = os.path.join(self.reports_dir, f'test_report_{timestamp}.html')
# 计算统计信息
total_tests = len(all_results)
passed_tests = sum(1 for r in all_results if r['success'])
failed_tests = total_tests - passed_tests
success_rate = (passed_tests / total_tests * 100) if total_tests > 0 else 0
# 按测试套件分组
test_suites = {}
for result in all_results:
suite_name = result['test_name'].split('测试')[0] if '测试' in result['test_name'] else '其他'
if suite_name not in test_suites:
test_suites[suite_name] = []
test_suites[suite_name].append(result)
html_content = f"""
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>浏览器自动化测试报告</title>
<style>
body {{
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
margin: 0;
padding: 20px;
background-color: #f5f5f5;
}}
.container {{
max-width: 1200px;
margin: 0 auto;
background: white;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
overflow: hidden;
}}
.header {{
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 30px;
text-align: center;
}}
.header h1 {{
margin: 0;
font-size: 2.5em;
}}
.header p {{
margin: 10px 0 0 0;
opacity: 0.9;
}}
.summary {{
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
padding: 30px;
background: #f8f9fa;
}}
.summary-card {{
background: white;
padding: 20px;
border-radius: 8px;
text-align: center;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}}
.summary-card h3 {{
margin: 0 0 10px 0;
color: #333;
}}
.summary-card .number {{
font-size: 2em;
font-weight: bold;
margin: 10px 0;
}}
.total {{ color: #6c757d; }}
.passed {{ color: #28a745; }}
.failed {{ color: #dc3545; }}
.rate {{ color: #007bff; }}
.test-suites {{
padding: 30px;
}}
.suite {{
margin-bottom: 30px;
border: 1px solid #e9ecef;
border-radius: 8px;
overflow: hidden;
}}
.suite-header {{
background: #e9ecef;
padding: 15px 20px;
font-weight: bold;
color: #495057;
}}
.test-item {{
padding: 15px 20px;
border-bottom: 1px solid #f8f9fa;
display: flex;
justify-content: space-between;
align-items: center;
}}
.test-item:last-child {{
border-bottom: none;
}}
.test-name {{
font-weight: 500;
}}
.test-status {{
padding: 5px 10px;
border-radius: 15px;
font-size: 0.9em;
font-weight: bold;
}}
.status-passed {{
background: #d4edda;
color: #155724;
}}
.status-failed {{
background: #f8d7da;
color: #721c24;
}}
.test-details {{
margin-top: 10px;
font-size: 0.9em;
color: #6c757d;
}}
.error-message {{
color: #dc3545;
font-style: italic;
}}
.screenshot-link {{
color: #007bff;
text-decoration: none;
margin-left: 10px;
}}
.screenshot-link:hover {{
text-decoration: underline;
}}
.footer {{
background: #f8f9fa;
padding: 20px;
text-align: center;
color: #6c757d;
border-top: 1px solid #e9ecef;
}}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🍎 Apple ERP 浏览器自动化测试报告</h1>
<p>测试时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</p>
</div>
<div class="summary">
<div class="summary-card">
<h3>总测试数</h3>
<div class="number total">{total_tests}</div>
</div>
<div class="summary-card">
<h3>通过</h3>
<div class="number passed">{passed_tests}</div>
</div>
<div class="summary-card">
<h3>失败</h3>
<div class="number failed">{failed_tests}</div>
</div>
<div class="summary-card">
<h3>成功率</h3>
<div class="number rate">{success_rate:.1f}%</div>
</div>
</div>
<div class="test-suites">
<h2>测试详情</h2>
"""
# 添加测试套件详情
for suite_name, tests in test_suites.items():
suite_passed = sum(1 for t in tests if t['success'])
suite_total = len(tests)
html_content += f"""
<div class="suite">
<div class="suite-header">
{suite_name} ({suite_passed}/{suite_total} 通过)
</div>
"""
for test in tests:
status_class = 'status-passed' if test['success'] else 'status-failed'
status_text = '通过' if test['success'] else '失败'
html_content += f"""
<div class="test-item">
<div>
<div class="test-name">{test['test_name']}</div>
<div class="test-details">
耗时: {test['duration']:.2f}秒
"""
if test['error']:
html_content += f'<div class="error-message">错误: {test["error"]}</div>'
if test['screenshots']:
for screenshot in test['screenshots']:
screenshot_name = os.path.basename(screenshot)
html_content += f'<a href="../screenshots/{screenshot_name}" class="screenshot-link" target="_blank">📷 截图</a>'
html_content += f"""
</div>
</div>
<div class="test-status {status_class}">{status_text}</div>
</div>
"""
html_content += "</div>"
html_content += f"""
</div>
<div class="footer">
<p>报告生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</p>
<p>Apple ERP 浏览器自动化测试框架</p>
</div>
</div>
</body>
</html>
"""
with open(report_file, 'w', encoding='utf-8') as f:
f.write(html_content)
return report_file
def generate_json_report(self, all_results: List[Dict[str, Any]]) -> str:
"""生成JSON测试报告"""
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
report_file = os.path.join(self.reports_dir, f'test_report_{timestamp}.json')
report_data = {
'timestamp': datetime.now().isoformat(),
'summary': {
'total_tests': len(all_results),
'passed_tests': sum(1 for r in all_results if r['success']),
'failed_tests': sum(1 for r in all_results if not r['success']),
'success_rate': (sum(1 for r in all_results if r['success']) / len(all_results) * 100) if all_results else 0
},
'results': all_results
}
with open(report_file, 'w', encoding='utf-8') as f:
json.dump(report_data, f, ensure_ascii=False, indent=2)
return report_file
def run_shared_browser_tests(self) -> List[Dict[str, Any]]:
"""运行共享浏览器实例的测试(订单管理 + 出库管理)"""
print("🚀 开始运行共享浏览器测试...")
print("=" * 60)
all_results = []
shared_browser = None
try:
# 创建共享的浏览器实例
shared_browser = BaseBrowserTest(headless=False, slow_mo=500)
shared_browser.setup_browser()
# 手动登录一次
print("开始登录...")
shared_browser.navigate_to(shared_browser.base_url)
shared_browser.fill_input('input[placeholder="请输入用户名"]', 'admin')
shared_browser.fill_input('input[placeholder="请输入密码"]', 'password')
shared_browser.click_element('button:has-text("登录")')
shared_browser.page.wait_for_timeout(5000)
# 检查登录是否成功
current_url = shared_browser.page.url
if 'dashboard' in current_url:
print("✅ 登录成功")
login_success = True
else:
print(f"❌ 登录失败,当前URL: {current_url}")
login_success = False
if not login_success:
print("❌ 登录失败,无法运行共享浏览器测试")
return []
# 1. 运行订单管理测试(使用共享浏览器)
print("\n📋 运行订单管理测试(共享浏览器)...")
try:
order_test = OrderManagementTest(headless=False, slow_mo=500)
# 直接使用已创建的浏览器实例
order_test.browser = shared_browser.browser
order_test.page = shared_browser.page
order_test.context = shared_browser.context
# 导航到订单管理页面
order_test.navigate_to(f'{order_test.base_url}/main/order')
order_test.wait_for_element('.order-container', timeout=15000)
print("✅ 成功进入订单管理页面")
# 运行订单管理测试用例
order_results = self._run_test_cases(order_test, [
("订单列表显示测试", order_test.test_order_list_display),
("按订单编号搜索测试", order_test.test_order_search_by_order_no),
("按经销商搜索测试", order_test.test_order_search_by_dealer),
("订单状态筛选测试", order_test.test_order_status_filter),
("重置搜索测试", order_test.test_reset_search),
("分页功能测试", order_test.test_pagination),
("表格操作测试", order_test.test_table_operations),
("新增订单功能测试", order_test.test_add_order),
("编辑订单功能测试", order_test.test_edit_order),
("查看订单功能测试", order_test.test_view_order),
])
all_results.extend(order_results)
print(f"✅ 订单管理测试完成,共 {len(order_results)} 个测试")
except Exception as e:
print(f"❌ 订单管理测试执行失败: {e}")
# 2. 运行出库管理测试(使用同一个浏览器实例)
print("\n📋 运行出库管理测试(共享浏览器)...")
try:
delivery_test = DeliveryManagementTest(headless=False, slow_mo=500)
# 直接使用已创建的浏览器实例
delivery_test.browser = shared_browser.browser
delivery_test.page = shared_browser.page
delivery_test.context = shared_browser.context
# 导航到出库管理页面
delivery_test.navigate_to(f'{delivery_test.base_url}/main/delivery')
delivery_test.wait_for_element('.delivery-container', timeout=15000)
print("✅ 成功进入出库管理页面")
# 运行出库管理测试用例
delivery_results = self._run_test_cases(delivery_test, [
("按出库单编号搜索测试", delivery_test.test_delivery_search_by_delivery_no),
("按经销商搜索测试", delivery_test.test_delivery_search_by_dealer),
("出库状态筛选测试", delivery_test.test_delivery_status_filter),
("重置搜索测试", delivery_test.test_delivery_reset_search),
("表格操作测试", delivery_test.test_delivery_table_operations),
("新增出库功能测试", delivery_test.test_add_delivery),
("编辑出库功能测试", delivery_test.test_edit_delivery),
("查看出库功能测试", delivery_test.test_view_delivery),
])
all_results.extend(delivery_results)
print(f"✅ 出库管理测试完成,共 {len(delivery_results)} 个测试")
except Exception as e:
print(f"❌ 出库管理测试执行失败: {e}")
finally:
# 最后关闭共享浏览器
if shared_browser:
shared_browser.teardown_browser()
print("✅ 共享浏览器已关闭")
return all_results
def _run_test_cases(self, test_instance, test_cases):
"""运行测试用例的通用方法"""
results = []
for test_name, test_func in test_cases:
print(f"\n{'='*50}")
print(f"开始测试: {test_name}")
print(f"{'='*50}")
start_time = datetime.now()
result = {
'test_name': test_name,
'start_time': start_time.isoformat(),
'success': False,
'error': None,
'screenshots': [],
'duration': 0
}
try:
test_func()
result['success'] = True
print(f"✅ 测试通过: {test_name}")
except Exception as e:
result['error'] = str(e)
print(f"❌ 测试失败: {test_name}")
print(f"错误信息: {e}")
screenshot_path = test_instance.take_screenshot(f'{test_name}_error')
result['screenshots'].append(screenshot_path)
finally:
end_time = datetime.now()
result['end_time'] = end_time.isoformat()
result['duration'] = (end_time - start_time).total_seconds()
print(f"测试耗时: {result['duration']:.2f}秒")
results.append(result)
return results
def run_all_tests(self) -> List[Dict[str, Any]]:
"""运行所有测试 - 顺序执行"""
print("🚀 开始运行浏览器自动化测试...")
print("=" * 60)
all_results = []
# 1. 运行登录测试
print("\n📋 运行登录测试...")
try:
login_results = run_login_tests()
all_results.extend(login_results)
print(f"✅ 登录测试完成,共 {len(login_results)} 个测试")
except Exception as e:
print(f"❌ 登录测试执行失败: {e}")
# 2. 运行共享浏览器测试(订单管理 + 出库管理)
print("\n📋 运行共享浏览器测试...")
try:
shared_results = self.run_shared_browser_tests()
all_results.extend(shared_results)
print(f"✅ 共享浏览器测试完成,共 {len(shared_results)} 个测试")
except Exception as e:
print(f"❌ 共享浏览器测试执行失败: {e}")
# 生成报告
print("\n📊 生成测试报告...")
html_report = self.generate_html_report(all_results)
json_report = self.generate_json_report(all_results)
# 打印总结
total_tests = len(all_results)
passed_tests = sum(1 for r in all_results if r['success'])
failed_tests = total_tests - passed_tests
success_rate = (passed_tests / total_tests * 100) if total_tests > 0 else 0
print("\n" + "=" * 60)
print("🎯 测试总结")
print("=" * 60)
print(f"总测试数: {total_tests}")
print(f"通过: {passed_tests}")
print(f"失败: {failed_tests}")
print(f"成功率: {success_rate:.1f}%")
print(f"\n📄 HTML报告: {html_report}")
print(f"📄 JSON报告: {json_report}")
print("=" * 60)
return all_results
if __name__ == '__main__':
runner = SimpleTestRunner()
runner.run_all_tests()