欢迎来到巧克力测试平台的"内功心法"!这里将揭秘平台的架构设计,让你了解这个测试平台是如何"炼成"的。
巧克力测试平台采用现代化的前后端分离架构:
- React 18: 现代化的前端框架
- TypeScript: 类型安全的JavaScript超集
- Ant Design Pro: 企业级UI解决方案
- UmiJS 4: 可扩展的企业级前端应用框架
- ProComponents: 高级组件库
- Flask: 轻量级Python Web框架
- SQLAlchemy: Python ORM框架
- Celery: 分布式任务队列
- Redis: 缓存和消息队列
- MySQL: 关系型数据库
- Pytest: Python测试框架
- Locust: 性能测试工具
- Requests: HTTP请求库
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 前端应用 │ │ 后端API │ │ 测试引擎 │
│ (React+TS) │◄──►│ (Flask) │◄──►│ (Pytest+Locust) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 静态资源 │ │ 数据存储 │ │ 任务队列 │
│ (CDN/Nginx) │ │ (MySQL+Redis) │ │ (Celery) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
src/
├── components/ # 公共组件
│ ├── RightContent/ # 右侧内容组件
│ └── Footer/ # 页脚组件
├── pages/ # 页面组件
│ ├── openapitest/ # 接口测试页面
│ ├── LocustTest/ # 压力测试页面
│ ├── Project/ # 项目管理页面
│ └── User/ # 用户相关页面
├── services/ # API服务层
│ ├── test_case/ # 测试用例服务
│ ├── test_suite/ # 测试套件服务
│ ├── test_plan/ # 测试计划服务
│ └── locust_*/ # Locust相关服务
├── models/ # 数据模型
├── utils/ # 工具函数
└── typings.d.ts # 类型定义
每个组件只负责一个特定的功能:
// 好的设计 - 单一职责
const TestCaseList = () => {
// 只负责展示测试用例列表
};
const TestCaseForm = () => {
// 只负责测试用例表单
};设计可复用的通用组件:
// 通用表格组件
interface ProTableProps<T> {
columns: ProColumns<T>[];
request: (params: any) => Promise<any>;
actionRef?: React.MutableRefObject<ActionType>;
}使用UmiJS的初始状态管理全局数据:
// app.tsx
export async function getInitialState() {
return {
currentUser: await fetchUserInfo(),
settings: defaultSettings,
};
}使用React Hooks管理页面级状态:
const [loading, setLoading] = useState(false);
const [dataSource, setDataSource] = useState([]);
const actionRef = useRef<ActionType>();// config/routes.ts
export default [
{
path: '/openapitest',
name: '接口测试',
icon: 'api',
routes: [
{
path: '/openapitest/case',
name: '用例管理',
component: './openapitest/caseList',
},
{
path: '/openapitest/suite',
name: '套件管理',
component: './openapitest/caseSuiteList',
},
],
},
];基于用户角色的路由权限控制:
// access.ts
export default function access(initialState) {
const { currentUser } = initialState ?? {};
return {
canAdmin: currentUser && currentUser.access === 1,
canTest: currentUser && currentUser.access === 0,
};
}┌─────────────────┐
│ Controller │ # 控制器层 - 处理HTTP请求
├─────────────────┤
│ Service │ # 服务层 - 业务逻辑处理
├─────────────────┤
│ DAO │ # 数据访问层 - 数据库操作
├─────────────────┤
│ Model │ # 模型层 - 数据模型定义
└─────────────────┘
# 资源命名规范
GET /api/auto_pytest/get_case # 获取测试用例
POST /api/auto_pytest/create_suite # 创建测试套件
PUT /api/auto_pytest/update_plan # 更新测试计划
DELETE /api/auto_pytest/delete_project # 删除项目{
"success": true,
"data": {
# 具体数据
},
"msg": "操作成功",
"code": 200
}-- 项目表
CREATE TABLE test_project (
id INT PRIMARY KEY AUTO_INCREMENT,
project_name VARCHAR(255) NOT NULL,
project_desc TEXT,
project_owners VARCHAR(255),
add_time DATETIME,
update_time DATETIME,
is_deleted BOOLEAN DEFAULT FALSE
);
-- 测试用例表
CREATE TABLE test_case (
id INT PRIMARY KEY AUTO_INCREMENT,
case_func VARCHAR(255) NOT NULL,
case_func_desc TEXT,
case_sence VARCHAR(255),
moudle_id INT,
path_desc TEXT,
tags VARCHAR(255),
update_time DATETIME
);
-- 测试套件表
CREATE TABLE test_suite (
id INT PRIMARY KEY AUTO_INCREMENT,
suite_name VARCHAR(255) NOT NULL,
describe TEXT,
project_id INT,
case_ids TEXT,
test_type VARCHAR(50),
test_env VARCHAR(50),
add_time DATETIME,
update_time DATETIME
);Project (1) ──── (N) TestSuite
TestSuite (N) ──── (N) TestCase
TestSuite (1) ──── (N) TestPlan
TestPlan (1) ──── (N) TestResult
# 测试执行流程
class TestExecutor:
def __init__(self, suite_config):
self.suite_config = suite_config
def execute(self):
# 1. 准备测试环境
self.setup_environment()
# 2. 执行测试用例
result = self.run_tests()
# 3. 收集测试结果
self.collect_results(result)
# 4. 生成测试报告
self.generate_report()# 压力测试执行流程
class LocustExecutor:
def __init__(self, locust_config):
self.config = locust_config
def start_test(self):
# 1. 启动Locust Master
self.start_master()
# 2. 启动Worker节点
self.start_workers()
# 3. 开始压力测试
self.begin_test()
# 4. 监控测试过程
self.monitor_test()# Celery任务定义
@celery.task
def execute_test_suite(suite_id, env):
"""异步执行测试套件"""
try:
# 执行测试
result = TestExecutor(suite_id, env).execute()
# 保存结果
save_test_result(result)
# 发送通知
send_notification(result)
except Exception as e:
# 错误处理
handle_error(e)# JWT Token认证
class AuthMiddleware:
def __init__(self):
self.secret_key = config.SECRET_KEY
def verify_token(self, token):
try:
payload = jwt.decode(token, self.secret_key)
return payload['user_id']
except jwt.ExpiredSignatureError:
raise AuthError('Token已过期')
except jwt.InvalidTokenError:
raise AuthError('无效Token')# 基于角色的权限控制
class PermissionManager:
PERMISSIONS = {
'admin': ['read', 'write', 'delete', 'manage'],
'tester': ['read', 'write'],
'viewer': ['read']
}
def check_permission(self, user_role, action):
return action in self.PERMISSIONS.get(user_role, [])- SQL注入防护: 使用ORM参数化查询
- XSS防护: 前端输入验证和转义
- CSRF防护: 使用CSRF Token
- 敏感数据加密: 密码和敏感配置加密存储
# 性能监控
class PerformanceMonitor:
def __init__(self):
self.metrics = {}
def record_api_call(self, endpoint, duration):
if endpoint not in self.metrics:
self.metrics[endpoint] = []
self.metrics[endpoint].append(duration)
def get_avg_response_time(self, endpoint):
times = self.metrics.get(endpoint, [])
return sum(times) / len(times) if times else 0# 结构化日志
import logging
import json
class StructuredLogger:
def __init__(self):
self.logger = logging.getLogger(__name__)
def log_api_request(self, request, response, duration):
log_data = {
'timestamp': datetime.now().isoformat(),
'method': request.method,
'url': request.url,
'status_code': response.status_code,
'duration': duration,
'user_id': request.user.id if request.user else None
}
self.logger.info(json.dumps(log_data))# Dockerfile示例
FROM node:16-alpine AS frontend-build
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
FROM python:3.9-slim AS backend
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
EXPOSE 5000
CMD ["python", "app.py"]# docker-compose.yml
version: '3.8'
services:
frontend:
build: ./frontend
ports:
- "3000:3000"
backend:
build: ./backend
ports:
- "5000:5000"
depends_on:
- mysql
- redis
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: password
redis:
image: redis:6.2-alpine
celery:
build: ./backend
command: celery worker -A app.celery
depends_on:
- redis- 单一职责原则: 每个类和函数只负责一个功能
- 开闭原则: 对扩展开放,对修改关闭
- 里氏替换原则: 子类可以替换父类
- 接口隔离原则: 使用多个专门的接口
- 依赖倒置原则: 依赖抽象而不是具体实现
- 插件化架构: 支持功能插件扩展
- 配置化驱动: 通过配置文件控制行为
- 事件驱动: 使用事件机制解耦组件
- 微服务化: 按业务域拆分服务
- 前端优化: 代码分割、懒加载、缓存策略
- 后端优化: 数据库索引、查询优化、缓存机制
- 网络优化: CDN、压缩、HTTP/2
- 系统优化: 负载均衡、集群部署
🎉 架构师! 你已经了解了巧克力测试平台的架构精髓。好的架构不是一蹴而就的,而是在实践中不断演进和优化的!