run_all_tests.py 14 KB
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
运行所有API测试脚本
Python 3.7.8 兼容
"""

import os
import sys
import json
import time
from datetime import datetime
from typing import Dict, List, Any

class TestReporter:
    """测试报告生成器"""
    
    def __init__(self):
        self.test_results = []
        self.start_time = None
        self.end_time = None
        
    def start_test_suite(self):
        """开始测试套件"""
        self.start_time = datetime.now()
        
    def end_test_suite(self):
        """结束测试套件"""
        self.end_time = datetime.now()
        
    def add_test_result(self, test_name: str, script_name: str, success: bool, 
                       execution_time: float, error_message: str = None, 
                       output: str = None):
        """添加测试结果"""
        result = {
            'test_name': test_name,
            'script_name': script_name,
            'success': success,
            'execution_time': execution_time,
            'error_message': error_message,
            'output': output,
            'timestamp': datetime.now().isoformat()
        }
        self.test_results.append(result)
        
    def generate_html_report(self, output_file: str = None):
        """生成HTML格式的测试报告"""
        if output_file is None:
            # 确保reports目录存在
            reports_dir = "reports"
            if not os.path.exists(reports_dir):
                os.makedirs(reports_dir)
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            output_file = os.path.join(reports_dir, f"test_report_{timestamp}.html")
            
        total_tests = len(self.test_results)
        passed_tests = sum(1 for r in self.test_results if r['success'])
        failed_tests = total_tests - passed_tests
        success_rate = (passed_tests / total_tests * 100) if total_tests > 0 else 0
        
        total_time = (self.end_time - self.start_time).total_seconds() if self.end_time and self.start_time else 0
        
        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>Apple ERP API 测试报告</title>
    <style>
        body {{ font-family: 'Microsoft YaHei', Arial, sans-serif; margin: 20px; background-color: #f5f5f5; }}
        .container {{ max-width: 1200px; margin: 0 auto; background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }}
        .header {{ text-align: center; margin-bottom: 30px; }}
        .header h1 {{ color: #333; margin-bottom: 10px; }}
        .header .timestamp {{ color: #666; font-size: 14px; }}
        .summary {{ display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin-bottom: 30px; }}
        .summary-card {{ background: #f8f9fa; padding: 20px; border-radius: 6px; text-align: center; border-left: 4px solid #007bff; }}
        .summary-card.success {{ border-left-color: #28a745; }}
        .summary-card.failure {{ border-left-color: #dc3545; }}
        .summary-card h3 {{ margin: 0 0 10px 0; color: #333; }}
        .summary-card .number {{ font-size: 2em; font-weight: bold; color: #007bff; }}
        .summary-card.success .number {{ color: #28a745; }}
        .summary-card.failure .number {{ color: #dc3545; }}
        .test-results {{ margin-top: 30px; }}
        .test-item {{ margin-bottom: 20px; padding: 15px; border-radius: 6px; border: 1px solid #ddd; }}
        .test-item.success {{ background: #d4edda; border-color: #c3e6cb; }}
        .test-item.failure {{ background: #f8d7da; border-color: #f5c6cb; }}
        .test-header {{ display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; }}
        .test-name {{ font-weight: bold; font-size: 16px; }}
        .test-status {{ padding: 4px 8px; border-radius: 4px; font-size: 12px; font-weight: bold; }}
        .test-status.success {{ background: #28a745; color: white; }}
        .test-status.failure {{ background: #dc3545; color: white; }}
        .test-details {{ font-size: 14px; color: #666; }}
        .test-output {{ background: #f8f9fa; padding: 10px; border-radius: 4px; margin-top: 10px; font-family: monospace; font-size: 12px; max-height: 200px; overflow-y: auto; }}
        .error-message {{ color: #dc3545; font-weight: bold; }}
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>🍎 Apple ERP API 测试报告</h1>
            <div class="timestamp">生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</div>
        </div>
        
        <div class="summary">
            <div class="summary-card">
                <h3>总测试数</h3>
                <div class="number">{total_tests}</div>
            </div>
            <div class="summary-card success">
                <h3>通过测试</h3>
                <div class="number">{passed_tests}</div>
            </div>
            <div class="summary-card failure">
                <h3>失败测试</h3>
                <div class="number">{failed_tests}</div>
            </div>
            <div class="summary-card">
                <h3>成功率</h3>
                <div class="number">{success_rate:.1f}%</div>
            </div>
            <div class="summary-card">
                <h3>执行时间</h3>
                <div class="number">{total_time:.2f}s</div>
            </div>
        </div>
        
        <div class="test-results">
            <h2>详细测试结果</h2>
"""
        
        for result in self.test_results:
            status_class = "success" if result['success'] else "failure"
            status_text = "✅ 通过" if result['success'] else "❌ 失败"
            
            html_content += f"""
            <div class="test-item {status_class}">
                <div class="test-header">
                    <div class="test-name">{result['test_name']}</div>
                    <div class="test-status {status_class}">{status_text}</div>
                </div>
                <div class="test-details">
                    <strong>脚本:</strong> {result['script_name']}<br>
                    <strong>执行时间:</strong> {result['execution_time']:.2f}秒<br>
                    <strong>时间戳:</strong> {result['timestamp']}
"""
            
            if result['error_message']:
                html_content += f'<div class="error-message">错误信息: {result["error_message"]}</div>'
                
            if result['output']:
                html_content += f'<div class="test-output">{result["output"]}</div>'
                
            html_content += """
                </div>
            </div>
"""
        
        html_content += """
        </div>
    </div>
</body>
</html>
"""
        
        with open(output_file, 'w', encoding='utf-8') as f:
            f.write(html_content)
            
        return output_file
        
    def generate_json_report(self, output_file: str = None):
        """生成JSON格式的测试报告"""
        if output_file is None:
            # 确保reports目录存在
            reports_dir = "reports"
            if not os.path.exists(reports_dir):
                os.makedirs(reports_dir)
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            output_file = os.path.join(reports_dir, f"test_report_{timestamp}.json")
            
        total_tests = len(self.test_results)
        passed_tests = sum(1 for r in self.test_results if r['success'])
        failed_tests = total_tests - passed_tests
        success_rate = (passed_tests / total_tests * 100) if total_tests > 0 else 0
        
        total_time = (self.end_time - self.start_time).total_seconds() if self.end_time and self.start_time else 0
        
        report_data = {
            'test_suite': 'Apple ERP API Tests',
            'generated_at': datetime.now().isoformat(),
            'summary': {
                'total_tests': total_tests,
                'passed_tests': passed_tests,
                'failed_tests': failed_tests,
                'success_rate': success_rate,
                'total_execution_time': total_time,
                'start_time': self.start_time.isoformat() if self.start_time else None,
                'end_time': self.end_time.isoformat() if self.end_time else None
            },
            'test_results': self.test_results
        }
        
        with open(output_file, 'w', encoding='utf-8') as f:
            json.dump(report_data, f, ensure_ascii=False, indent=2)
            
        return output_file

def main():
    """主函数:按顺序运行所有测试"""
    print("=" * 60)
    print("Apple ERP API 测试套件")
    print("=" * 60)
    
    # 初始化测试报告器
    reporter = TestReporter()
    reporter.start_test_suite()
    
    # 测试脚本列表(按执行顺序)
    tests = [
        ("认证登录测试", "test_auth.py"),
        ("用户管理测试", "test_users.py"),
        ("角色管理测试", "test_roles.py"),
        ("菜单管理测试", "test_menus.py"),
        ("字典管理测试", "test_dicts.py"),
        ("产品信息管理测试", "test_products.py"),
        ("订单管理测试", "test_orders.py"),
    ]
    
    success_count = 0
    total_count = len(tests)
    
    for test_name, script_name in tests:
        print(f"\n{'='*20} {test_name} {'='*20}")
        
        if not os.path.exists(script_name):
            print(f"❌ 脚本不存在: {script_name}")
            reporter.add_test_result(test_name, script_name, False, 0, 
                                   f"脚本不存在: {script_name}")
            continue
        
        start_time = time.time()
        test_output = []
        error_message = None
        test_success = False
        
        try:
            # 直接导入并运行测试函数
            module_name = script_name.replace('.py', '')
            if module_name == 'test_auth':
                from test_auth import login_and_print_token
                success, _ = login_and_print_token('admin', 'password')
                if success:
                    print(f"✅ {test_name} 执行成功")
                    success_count += 1
                    test_success = True
                else:
                    print(f"❌ {test_name} 执行失败")
                    error_message = "登录失败"
            elif module_name == 'test_users':
                from test_users import run_user_tests
                run_user_tests()
                print(f"✅ {test_name} 执行成功")
                success_count += 1
                test_success = True
            elif module_name == 'test_roles':
                from test_roles import run_role_tests
                run_role_tests()
                print(f"✅ {test_name} 执行成功")
                success_count += 1
                test_success = True
            elif module_name == 'test_menus':
                from test_menus import run_menu_tests
                run_menu_tests()
                print(f"✅ {test_name} 执行成功")
                success_count += 1
                test_success = True
            elif module_name == 'test_dicts':
                from test_dicts import run_dict_tests
                run_dict_tests()
                print(f"✅ {test_name} 执行成功")
                success_count += 1
                test_success = True
            elif module_name == 'test_products':
                from test_products import main as run_product_tests
                # 重定向stdout来捕获输出
                import io
                from contextlib import redirect_stdout
                
                output_buffer = io.StringIO()
                with redirect_stdout(output_buffer):
                    try:
                        exit_code = run_product_tests()
                        test_success = (exit_code == 0)
                        test_output.append(output_buffer.getvalue())
                    except SystemExit as e:
                        test_success = (e.code == 0)
                        test_output.append(output_buffer.getvalue())
                    except Exception as e:
                        test_success = False
                        error_message = str(e)
                        test_output.append(output_buffer.getvalue())
                
                if test_success:
                    print(f"✅ {test_name} 执行成功")
                    success_count += 1
                else:
                    print(f"❌ {test_name} 执行失败")
                    if not error_message:
                        error_message = "产品管理API测试失败"
            elif module_name == 'test_orders':
                from test_orders import run_order_tests
                run_order_tests()
                print(f"✅ {test_name} 执行成功")
                success_count += 1
                test_success = True
                
        except Exception as e:
            print(f"❌ {test_name} 执行失败: {e}")
            error_message = str(e)
        
        execution_time = time.time() - start_time
        reporter.add_test_result(test_name, script_name, test_success, 
                               execution_time, error_message, 
                               '\n'.join(test_output) if test_output else None)
    
    # 结束测试套件
    reporter.end_test_suite()
    
    # 总结
    print("\n" + "=" * 60)
    print(f"测试完成: {success_count}/{total_count} 个测试通过")
    print("=" * 60)
    
    # 生成测试报告
    try:
        html_report = reporter.generate_html_report()
        json_report = reporter.generate_json_report()
        
        print(f"\n📊 测试报告已生成:")
        print(f"   HTML报告: {html_report}")
        print(f"   JSON报告: {json_report}")
        
    except Exception as e:
        print(f"⚠️  生成测试报告时出错: {e}")
    
    if success_count == total_count:
        print("🎉 所有测试都通过了!")
        return 0
    else:
        print("⚠️  部分测试失败,请检查输出信息")
        return 1

if __name__ == "__main__":
    sys.exit(main())