#!/bin/bash # FendxPHP 自动化测试脚本 # 用法: ./scripts/run-tests.sh [test_type] [options] set -e # 颜色定义 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # 配置 PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" TEST_RESULTS_DIR="${PROJECT_ROOT}/reports" LOG_FILE="${TEST_RESULTS_DIR}/test.log" # 创建必要的目录 mkdir -p "${TEST_RESULTS_DIR}" mkdir -p "${TEST_RESULTS_DIR}/coverage" mkdir -p "${TEST_RESULTS_DIR}/performance" # 日志函数 log() { echo -e "$1" | tee -a "${LOG_FILE}" } log_info() { log "${BLUE}[INFO]${NC} $1" } log_success() { log "${GREEN}[SUCCESS]${NC} $1" } log_warning() { log "${YELLOW}[WARNING]${NC} $1" } log_error() { log "${RED}[ERROR]${NC} $1" } # 显示帮助信息 show_help() { cat << EOF FendxPHP 自动化测试脚本 用法: $0 [test_type] [options] 测试类型: unit 运行单元测试 integration 运行集成测试 api 运行API测试 e2e 运行端到端测试 performance 运行性能测试 security 运行安全测试 all 运行所有测试 (默认) 选项: --coverage 生成覆盖率报告 --parallel=N 并行运行测试 (N个进程) --filter=FILTER 过滤测试用例 --verbose 详细输出 --no-docker 不使用Docker环境 --clean 清理测试环境 --help 显示此帮助信息 示例: $0 unit --coverage $0 integration --filter=UserTest $0 all --parallel=4 --coverage $0 performance --verbose EOF } # 检查依赖 check_dependencies() { log_info "检查依赖..." # 检查PHP if ! command -v php &> /dev/null; then log_error "PHP 未安装" exit 1 fi # 检查Composer if ! command -v composer &> /dev/null; then log_error "Composer 未安装" exit 1 fi # 检查PHPUnit if ! command -v vendor/bin/phpunit &> /dev/null; then log_info "安装 PHPUnit..." composer require --dev phpunit/phpunit fi # 检查Docker (如果需要) if [[ "$USE_DOCKER" == "true" ]]; then if ! command -v docker &> /dev/null; then log_error "Docker 未安装" exit 1 fi fi log_success "依赖检查完成" } # 准备测试环境 prepare_environment() { log_info "准备测试环境..." # 复制环境配置 if [[ ! -f "${PROJECT_ROOT}/.env" ]]; then cp "${PROJECT_ROOT}/.env.example" "${PROJECT_ROOT}/.env" fi # 设置测试环境变量 export APP_ENV=testing export DB_CONNECTION=sqlite export DB_DATABASE=:memory: export CACHE_DRIVER=array export SESSION_DRIVER=array export QUEUE_CONNECTION=sync # 安装依赖 log_info "安装Composer依赖..." composer install --optimize-autoloader --no-interaction # 创建测试数据库 log_info "创建测试数据库..." php bin/console migrate:run --env=testing log_success "测试环境准备完成" } # 启动Docker测试环境 start_docker_environment() { if [[ "$USE_DOCKER" != "true" ]]; then return fi log_info "启动Docker测试环境..." # 停止现有容器 docker-compose -f docker-compose.test.yml down --volumes 2>/dev/null || true # 启动测试容器 docker-compose -f docker-compose.test.yml up -d --build # 等待服务就绪 log_info "等待服务启动..." sleep 30 # 检查服务状态 if ! docker-compose -f docker-compose.test.yml ps | grep -q "Up"; then log_error "Docker服务启动失败" docker-compose -f docker-compose.test.yml logs exit 1 fi log_success "Docker环境启动完成" } # 停止Docker测试环境 stop_docker_environment() { if [[ "$USE_DOCKER" != "true" ]]; then return fi log_info "停止Docker测试环境..." docker-compose -f docker-compose.test.yml down --volumes } # 运行单元测试 run_unit_tests() { log_info "运行单元测试..." local cmd="vendor/bin/phpunit tests/Unit --colors=always --log-junit=${TEST_RESULTS_DIR}/junit-unit.xml" if [[ "$GENERATE_COVERAGE" == "true" ]]; then cmd="$cmd --coverage-html=${TEST_RESULTS_DIR}/coverage/unit --coverage-clover=${TEST_RESULTS_DIR}/coverage/unit.xml" fi if [[ -n "$FILTER" ]]; then cmd="$cmd --filter=$FILTER" fi if [[ "$VERBOSE" == "true" ]]; then cmd="$cmd --verbose" fi if [[ "$PARALLEL" -gt 1 ]]; then cmd="$cmd --processes=$PARALLEL" fi log_info "执行: $cmd" if eval "$cmd"; then log_success "单元测试通过" return 0 else log_error "单元测试失败" return 1 fi } # 运行集成测试 run_integration_tests() { log_info "运行集成测试..." local cmd="vendor/bin/phpunit tests/Integration --colors=always --log-junit=${TEST_RESULTS_DIR}/junit-integration.xml" if [[ -n "$FILTER" ]]; then cmd="$cmd --filter=$FILTER" fi if [[ "$VERBOSE" == "true" ]]; then cmd="$cmd --verbose" fi log_info "执行: $cmd" if eval "$cmd"; then log_success "集成测试通过" return 0 else log_error "集成测试失败" return 1 fi } # 运行API测试 run_api_tests() { log_info "运行API测试..." # 启动应用服务器 log_info "启动应用服务器..." php -S localhost:8000 -t public/ > /dev/null 2>&1 & local SERVER_PID=$! # 等待服务器启动 sleep 5 # 运行API测试 local cmd="vendor/bin/codecept run api --colors --xml=${TEST_RESULTS_DIR}/api-results.xml" if [[ -n "$FILTER" ]]; then cmd="$cmd -g $FILTER" fi log_info "执行: $cmd" local result=0 if ! eval "$cmd"; then log_error "API测试失败" result=1 else log_success "API测试通过" fi # 停止服务器 kill $SERVER_PID 2>/dev/null || true return $result } # 运行端到端测试 run_e2e_tests() { log_info "运行端到端测试..." # 启动完整环境 start_docker_environment # 运行E2E测试 local cmd="vendor/bin/codecept run e2e --colors --xml=${TEST_RESULTS_DIR}/e2e-results.xml" if [[ -n "$FILTER" ]]; then cmd="$cmd -g $FILTER" fi log_info "执行: $cmd" local result=0 if ! eval "$cmd"; then log_error "E2E测试失败" result=1 else log_success "E2E测试通过" fi # 清理环境 stop_docker_environment return $result } # 运行性能测试 run_performance_tests() { log_info "运行性能测试..." # 启动应用服务器 php -S localhost:8000 -t public/ > /dev/null 2>&1 & local SERVER_PID=$! sleep 5 # 并发测试 log_info "执行并发测试..." ab -n 1000 -c 10 http://localhost:8000/api/users > "${TEST_RESULTS_DIR}/performance/ab-results.txt" 2>&1 # 内存测试 log_info "执行内存测试..." php -d memory_limit=1G -r " \$start = memory_get_usage(); for (\$i = 0; \$i < 10000; \$i++) { \$data = ['id' => \$i, 'name' => 'test']; json_encode(\$data); } echo 'Memory used: ' . ((memory_get_usage() - \$start) / 1024 / 1024) . ' MB\n'; " > "${TEST_RESULTS_DIR}/performance/memory-test.txt" # 数据库性能测试 log_info "执行数据库性能测试..." php bin/console benchmark:database > "${TEST_RESULTS_DIR}/performance/database-test.txt" 2>&1 # 停止服务器 kill $SERVER_PID 2>/dev/null || true log_success "性能测试完成" return 0 } # 运行安全测试 run_security_tests() { log_info "运行安全测试..." # 依赖漏洞扫描 log_info "扫描依赖漏洞..." composer audit > "${TEST_RESULTS_DIR}/security/composer-audit.txt" 2>&1 # 代码安全分析 log_info "执行代码安全分析..." if command -v vendor/bin/phpstan &> /dev/null; then vendor/bin/phpstan analyse --level=8 --error-format=json > "${TEST_RESULTS_DIR}/security/phpstan-results.json" 2>&1 fi # API安全测试 log_info "执行API安全测试..." if command -v vendor/bin/codecept &> /dev/null; then vendor/bin/codecept run security --colors --xml="${TEST_RESULTS_DIR}/security-results.xml" 2>&1 || true fi log_success "安全测试完成" return 0 } # 生成测试报告 generate_report() { log_info "生成测试报告..." local report_file="${TEST_RESULTS_DIR}/test-report.html" cat > "$report_file" << EOF
生成时间: $(date)
测试类型: ${TEST_TYPE}
| 测试类型 | 状态 | 详情 |
|---|---|---|
| 单元测试 | 通过 | 查看覆盖率 |
| 集成测试 | 通过 | 查看详细日志 |
| 指标 | 值 |
|---|---|
| 每秒请求数 | ${rps} |