OEMSaaS 自动化测试配置指南
📋 概述
本文档介绍两种自动化测试方案:
| 方案 | 工具 | 验证内容 | 适用场景 |
|---|---|---|---|
| 方案一 | PHPUnit + Git Hooks | 代码逻辑、单元测试 | 每次commit自动测试 |
| 方案二 | Shell + curl + MCP | API接口、数据库验证 | 端到端功能测试 |
方案一:PHPUnit + Git Pre-Commit Hook
1.1 安装 PHPUnit
cd /Users/lm/www/oemsaas
composer require --dev phpunit/phpunit:^9.01.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/feature1.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
fi1.5 激活 Hook
chmod +x /Users/lm/www/oemsaas/.git/hooks/pre-commit1.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/cases2.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_error2.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_error2.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 12.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 $FAILED2.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
fiQ4: 怎么清理测试数据?
在测试用例中添加 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 -1Elasticsearch
mcp_elasticsearch-mcp_list_indices
mcp_elasticsearch-mcp_get_document --index "索引名" --id "文档ID"
mcp_elasticsearch-mcp_search_documents --index "索引名" --body '{"query":{"match_all":{}}}'下一步
- ✅ 按照本文档配置测试环境
- ✅ 创建第一个测试用例
- ✅ 运行测试验证配置
- ✅ 将测试集成到开发流程
文档创建时间: 2026-04-23 项目路径: /Users/lm/www/oemsaas