OEMSaaS 自动化测试配置指南

📋 概述

本文档介绍两种自动化测试方案:

方案工具验证内容适用场景
方案一PHPUnit + Git Hooks代码逻辑、单元测试每次commit自动测试
方案二Shell + curl + MCPAPI接口、数据库验证端到端功能测试

方案一:PHPUnit + Git Pre-Commit Hook

1.1 安装 PHPUnit

cd /Users/lm/www/oemsaas
 
composer require --dev phpunit/phpunit:^9.0

1.2 创建 PHPUnit 配置文件

文件位置: /Users/lm/www/oemsaas/phpunit.xml

<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
         bootstrap="vendor/autoload.php"
         colors="true"
         stopOnFailure="false"
         cacheResult="false"
         executionOrder="depends,defects">
    <testsuites>
        <testsuite name="Unit">
            <directory suffix="Test.php">./tests/unit</directory>
        </testsuite>
        <testsuite name="Feature">
            <directory suffix="Test.php">./tests/feature</directory>
        </testsuite>
    </testsuites>
    <php>
        <env name="APP_ENV" value="testing"/>
        <env name="STORE_ID" value="1"/>
    </php>
</phpunit>

1.3 创建测试目录

mkdir -p /Users/lm/www/oemsaas/tests/unit
mkdir -p /Users/lm/www/oemsaas/tests/feature

1.4 创建 Git Pre-Commit Hook

文件位置: /Users/lm/www/oemsaas/.git/hooks/pre-commit

#!/bin/bash
 
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
 
PROJECT_ROOT="$(git rev-parse --show-toplevel)"
 
echo -e "${BLUE}============================================${NC}"
echo -e "${BLUE}     Git Pre-Commit Hook - PHPUnit${NC}"
echo -e "${BLUE}============================================${NC}"
 
cd "$PROJECT_ROOT" || exit 1
 
# 检查PHPUnit是否安装
if [ ! -f "./vendor/bin/phpunit" ]; then
    echo -e "${YELLOW}[WARNING] PHPUnit 未安装,跳过测试${NC}"
    exit 0
fi
 
# 检查是否有测试文件
if [ ! -d "./tests" ]; then
    echo -e "${YELLOW}[WARNING] tests 目录不存在,跳过测试${NC}"
    exit 0
fi
 
echo ""
echo -e "${GREEN}[INFO] 检查暂存的改动...${NC}"
 
# 获取暂存的PHP文件
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM)
PHP_FILES=$(echo "$STAGED_FILES" | grep -E '\.php$' || true)
 
if [ -z "$PHP_FILES" ]; then
    echo -e "${GREEN}[INFO] 没有暂存的 PHP 文件,跳过测试${NC}"
    exit 0
fi
 
echo -e "${GREEN}[INFO] 运行 PHPUnit 测试...${NC}"
echo "============================================"
 
# 运行 PHPUnit
./vendor/bin/phpunit --testdox
 
RESULT=$?
 
echo "============================================"
 
if [ $RESULT -eq 0 ]; then
    echo -e "${GREEN}✅ 所有测试通过!可以提交了!${NC}"
    exit 0
else
    echo -e "${RED}❌ 测试失败!请修复后再提交!${NC}"
    exit 1
fi

1.5 激活 Hook

chmod +x /Users/lm/www/oemsaas/.git/hooks/pre-commit

1.6 创建测试用例

文件位置: /Users/lm/www/oemsaas/tests/unit/BaseServiceTest.php

<?php
 
namespace tests\unit;
 
use PHPUnit\Framework\TestCase;
use common\BaseService;
 
class BaseServiceTest extends TestCase
{
    public function testServiceInstantiation()
    {
        $service = new BaseService();
        $this->assertInstanceOf(BaseService::class, $service);
    }
 
    public function testStoreIdDefaults()
    {
        $service = new BaseService();
        $this->assertIsInt($service->storeId);
    }
}

1.7 运行测试

# 运行所有测试
./vendor/bin/phpunit
 
# 详细输出
./vendor/bin/phpunit --testdox
 
# 运行特定测试文件
./vendor/bin/phpunit tests/unit/BaseServiceTest.php
 
# 运行特定测试套件
./vendor/bin/phpunit --testsuite=Unit

方案二:Shell脚本 + curl + MCP 测试

2.1 创建测试目录结构

mkdir -p /Users/lm/www/oemsaas/tests/scripts
mkdir -p /Users/lm/www/oemsaas/tests/cases

2.2 创建 API 测试封装脚本

文件位置: /Users/lm/www/oemsaas/tests/scripts/api_tester.sh

#!/bin/bash
 
BASE_URL="http://127.0.0.1:8000"
API_PREFIX="/api"
 
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
 
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
 
# 发送API请求
# 参数: $1=方法 $2=路径 $3=body(可选)
api_request() {
    local method="$1"
    local path="$2"
    local body="$3"
    local full_url="${BASE_URL}${API_PREFIX}${path}"
 
    log_info "==> ${method} ${full_url}"
 
    if [[ -n "$body" ]]; then
        curl -s -X "$method" "$full_url" \
            -H 'Content-Type: application/json' \
            -d "$body"
    else
        curl -s -X "$method" "$full_url"
    fi
}
 
# POST请求
api_post() {
    api_request "POST" "$1" "$2"
}
 
# GET请求
api_get() {
    api_request "GET" "$1"
}
 
# 验证响应码
verify_response() {
    local response="$1"
    local expected_code="${2:-0}"
 
    local actual_code=$(echo "$response" | grep -o '"code":[0-9-]*' | head -1 | cut -d':' -f2)
 
    if [[ "$actual_code" == "$expected_code" ]]; then
        log_info "响应码验证通过: code=${actual_code}"
        return 0
    else
        log_error "响应码验证失败: 期望=${expected_code}, 实际=${actual_code}"
        return 1
    fi
}
 
export -f api_request api_post api_get verify_response
export -f log_info log_error

2.3 创建数据库验证脚本

文件位置: /Users/lm/www/oemsaas/tests/scripts/db_verifier.sh

#!/bin/bash
 
RED='\033[0;31m'
GREEN='\033[0;32m'
NC='\033[0m'
 
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
 
# MySQL查询验证
mysql_verify() {
    local database="$1"
    local sql="$2"
    local expected="$3"
 
    log_info "MySQL验证: $sql"
 
    local result=$(mcp_mysql-rds_execute_query --database "$database" --query "$sql" 2>/dev/null)
 
    if [[ -z "$result" ]]; then
        log_error "MySQL查询无结果"
        return 1
    fi
 
    if [[ -n "$expected" && "$result" == *"$expected"* ]]; then
        log_info "✅ MySQL验证通过"
        return 0
    else
        log_error "❌ MySQL验证失败"
        return 1
    fi
}
 
# Redis验证
redis_verify() {
    local key="$1"
    local expected="$2"
 
    log_info "Redis验证: key=$key"
 
    local result=$(mcp_redis_get --key "$key" 2>/dev/null)
 
    if [[ $? -ne 0 || -z "$result" ]]; then
        log_error "Redis查询失败或key不存在"
        return 1
    fi
 
    if [[ -n "$expected" && "$result" == *"$expected"* ]]; then
        log_info "✅ Redis验证通过"
        return 0
    else
        log_error "❌ Redis验证失败"
        return 1
    fi
}
 
# Elasticsearch文档验证
es_verify() {
    local index="$1"
    local doc_id="$2"
    local expected="$3"
 
    log_info "ES验证: index=$index, id=$doc_id"
 
    local result=$(mcp_elasticsearch-mcp_get_document --index "$index" --id "$doc_id" 2>/dev/null)
 
    if [[ $? -ne 0 || -z "$result" || "$result" == *"null"* ]]; then
        log_error "ES文档不存在或查询失败"
        return 1
    fi
 
    if [[ -n "$expected" && "$result" == *"$expected"* ]]; then
        log_info "✅ ES验证通过"
        return 0
    else
        log_error "❌ ES验证失败"
        return 1
    fi
}
 
export -f mysql_verify redis_verify es_verify
export -f log_info log_error

2.4 创建测试用例示例

文件位置: /Users/lm/www/oemsaas/tests/cases/auth_test.sh

#!/bin/bash
 
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${SCRIPT_DIR}/../scripts/api_tester.sh"
source "${SCRIPT_DIR}/../scripts/db_verifier.sh"
 
TEST_DB="oemsaas_cn01"
ADMIN_ID=1
 
TOTAL=0
PASSED=0
FAILED=0
 
echo "============================================"
echo "     Auth 模块自动化测试"
echo "============================================"
 
test_save_admin_auth() {
    TOTAL=$((TOTAL + 1))
    log_info "=== 测试${TOTAL}: 保存管理员权限 ==="
 
    local auth_keys="menu_view,product_list,order_create"
    local response=$(api_post "/auth/saveAdminAuth" \
        "{\"authKeys\":\"$auth_keys\"}")
 
    echo "响应: $response"
 
    if verify_response "$response" "0"; then
        PASSED=$((PASSED + 1))
    else
        FAILED=$((FAILED + 1))
    fi
}
 
test_invalid_auth() {
    TOTAL=$((TOTAL + 1))
    log_info "=== 测试${TOTAL}: 异常参数验证 ==="
 
    local response=$(api_post "/auth/saveAdminAuth" "{\"authKeys\":\"\"}")
 
    local actual_code=$(echo "$response" | grep -o '"code":[0-9-]*' | head -1 | cut -d':' -f2)
 
    if [[ "$actual_code" != "0" ]]; then
        log_info "✅ 异常参数正确拒绝"
        PASSED=$((PASSED + 1))
    else
        log_error "❌ 异常参数未被处理"
        FAILED=$((FAILED + 1))
    fi
}
 
# 运行测试
test_save_admin_auth
test_invalid_auth
 
echo ""
echo "============================================"
echo "测试结果: 通过=$PASSED, 失败=$FAILED"
echo "============================================"
 
[[ $FAILED -eq 0 ]] && exit 0 || exit 1

2.5 创建测试入口脚本

文件位置: /Users/lm/www/oemsaas/tests/scripts/run_tests.sh

#!/bin/bash
 
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CASES_DIR="${SCRIPT_DIR}/cases"
 
echo "============================================"
echo "     OEMSaaS 自动化测试平台"
echo "============================================"
echo "测试时间: $(date '+%Y-%m-%d %H:%M:%S')"
echo "============================================"
 
# 检查MCP服务
echo ""
echo "检查MCP服务状态..."
 
mcp_mysql-rds_list_databases > /dev/null 2>&1 && echo "  ✅ MySQL" || echo "  ⚠️  MySQL"
mcp_redis_ping > /dev/null 2>&1 && echo "  ✅ Redis" || echo "  ⚠️  Redis"
mcp_elasticsearch-mcp_get_cluster_health > /dev/null 2>&1 && echo "  ✅ ES" || echo "  ⚠️  ES"
 
echo ""
echo "开始执行测试..."
echo "============================================"
 
TOTAL=0
PASSED=0
FAILED=0
 
for test_file in "${CASES_DIR}"/*.sh; do
    if [[ -f "$test_file" ]]; then
        test_name=$(basename "$test_file" .sh)
        echo ""
        echo ">>> 运行测试: $test_name"
 
        bash "$test_file"
        result=$?
 
        TOTAL=$((TOTAL + 1))
 
        if [[ $result -eq 0 ]]; then
            echo ">>> $test_name: ✅ 通过"
            PASSED=$((PASSED + 1))
        else
            echo ">>> $test_name: ❌ 失败"
            FAILED=$((FAILED + 1))
        fi
    fi
done
 
echo ""
echo "============================================"
echo "📊 测试报告"
echo "============================================"
echo "总测试数: $TOTAL"
echo "通过:     $PASSED"
echo "失败:     $FAILED"
echo "============================================"
 
[[ $FAILED -eq 0 ]] && echo "🎉 全部测试通过!" || echo "⚠️  存在失败测试!"
exit $FAILED

2.6 运行 Shell 测试

chmod +x /Users/lm/www/oemsaas/tests/scripts/*.sh
chmod +x /Users/lm/www/oemsaas/tests/cases/*.sh
 
# 启动ThinkPHP服务
php think run 8000 &
 
# 运行所有测试
bash tests/scripts/run_tests.sh
 
# 运行单个测试
bash tests/cases/auth_test.sh

方案三:两种方案结合(推荐)

3.1 日常工作流

开发新功能
    ↓
编写 PHPUnit 单元测试 (tests/unit/)
    ↓
git commit → 自动运行 PHPUnit
    ↓
如果需要端到端测试
    ↓
编写 Shell 测试用例 (tests/cases/)
    ↓
运行 bash tests/scripts/run_tests.sh

3.2 文件结构

oemsaas/
├── phpunit.xml                    # PHPUnit配置
├── tests/
│   ├── unit/                      # PHPUnit单元测试
│   │   └── *Test.php
│   ├── feature/                   # PHPUnit功能测试
│   │   └── *Test.php
│   └── cases/                     # Shell脚本测试
│       └── *_test.sh
└── .git/hooks/
    └── pre-commit                 # Git Hook

常见问题

Q1: 如何跳过测试强制提交?

git commit --no-verify -m "紧急修复"

Q2: 测试依赖数据库怎么办?

phpunit.xml 中配置数据库连接:

<php>
    <env name="APP_ENV" value="testing"/>
    <env name="DB_CONNECTION" value="mysql"/>
    <env name="DB_HOST" value="127.0.0.1"/>
    <env name="DB_DATABASE" value="oemsaas_test"/>
</php>

Q3: 如何只测试修改的文件?

修改 pre-commit 钩子,只运行相关测试:

# 在pre-commit中添加
PHP_FILES=$(echo "$STAGED_FILES" | grep -E '\.php$' || true)
 
# 根据文件路径运行对应测试
if [[ "$PHP_FILES" == *"AdminAuthService"* ]]; then
    ./vendor/bin/phpunit tests/unit/AdminAuthServiceTest.php
fi

Q4: 怎么清理测试数据?

在测试用例中添加 cleanup:

# Shell测试后清理
mysql_verify "$TEST_DB" "DELETE FROM o_customer WHERE name LIKE '测试%'" ""

MCP 工具参考

MySQL

mcp_mysql-rds_list_databases
mcp_mysql-rds_list_tables --database "数据库名"
mcp_mysql-rds_describe_table --database "数据库名" --table "表名"
mcp_mysql-rds_execute_query --database "数据库名" --query "SQL语句"

Redis

mcp_redis_get --key "键名"
mcp_redis_hgetall --key "哈希键名"
mcp_redis_smembers --key "集合键名"
mcp_redis_lrange --key "列表键名" --start 0 --stop -1

Elasticsearch

mcp_elasticsearch-mcp_list_indices
mcp_elasticsearch-mcp_get_document --index "索引名" --id "文档ID"
mcp_elasticsearch-mcp_search_documents --index "索引名" --body '{"query":{"match_all":{}}}'

下一步

  1. ✅ 按照本文档配置测试环境
  2. ✅ 创建第一个测试用例
  3. ✅ 运行测试验证配置
  4. ✅ 将测试集成到开发流程

文档创建时间: 2026-04-23 项目路径: /Users/lm/www/oemsaas