Skip to content

Latest commit

 

History

History
657 lines (548 loc) · 15.9 KB

File metadata and controls

657 lines (548 loc) · 15.9 KB

✨ 最佳实践

欢迎来到巧克力测试平台的"武功秘籍"!这里汇集了前人栽树的经验,让后人乘凉的智慧。掌握这些最佳实践,让你的代码更优雅、更高效、更可维护!

🎯 总体原则

SOLID原则

  1. 单一职责原则 (SRP): 一个类只负责一个功能
  2. 开闭原则 (OCP): 对扩展开放,对修改关闭
  3. 里氏替换原则 (LSP): 子类可以替换父类
  4. 接口隔离原则 (ISP): 使用多个专门的接口
  5. 依赖倒置原则 (DIP): 依赖抽象而不是具体实现

DRY原则

Don't Repeat Yourself - 避免重复代码,提取公共逻辑

KISS原则

Keep It Simple, Stupid - 保持简单,避免过度设计

🎨 前端开发最佳实践

组件设计原则

1. 组件职责单一

// ❌ 不好的设计 - 职责混乱
const UserManagement = () => {
  // 用户列表逻辑
  // 用户表单逻辑
  // 权限管理逻辑
  // 数据统计逻辑
};

// ✅ 好的设计 - 职责清晰
const UserList = () => {
  // 只负责用户列表展示
};

const UserForm = () => {
  // 只负责用户表单
};

const UserPermission = () => {
  // 只负责权限管理
};

2. Props接口设计

// ✅ 清晰的Props接口
interface TestCaseListProps {
  dataSource: TestCase[];
  loading?: boolean;
  onEdit?: (record: TestCase) => void;
  onDelete?: (id: number) => void;
  onRefresh?: () => void;
}

const TestCaseList: React.FC<TestCaseListProps> = ({
  dataSource,
  loading = false,
  onEdit,
  onDelete,
  onRefresh,
}) => {
  // 组件实现
};

3. 自定义Hook

// ✅ 提取业务逻辑到自定义Hook
const useTestCaseList = () => {
  const [dataSource, setDataSource] = useState<TestCase[]>([]);
  const [loading, setLoading] = useState(false);
  
  const fetchData = useCallback(async (params: any) => {
    setLoading(true);
    try {
      const response = await getCase(params);
      setDataSource(response.data);
    } catch (error) {
      message.error('获取数据失败');
    } finally {
      setLoading(false);
    }
  }, []);
  
  return {
    dataSource,
    loading,
    fetchData,
  };
};

状态管理最佳实践

1. 状态提升

// ✅ 将共享状态提升到父组件
const TestSuiteManagement = () => {
  const [selectedSuite, setSelectedSuite] = useState<TestSuite | null>(null);
  
  return (
    <div>
      <TestSuiteList 
        onSelect={setSelectedSuite}
        selectedId={selectedSuite?.id}
      />
      <TestSuiteDetail 
        suite={selectedSuite}
        onUpdate={handleUpdate}
      />
    </div>
  );
};

2. 使用useReducer管理复杂状态

interface TestPlanState {
  plans: TestPlan[];
  loading: boolean;
  selectedPlan: TestPlan | null;
  filters: FilterParams;
}

type TestPlanAction = 
  | { type: 'SET_LOADING'; payload: boolean }
  | { type: 'SET_PLANS'; payload: TestPlan[] }
  | { type: 'SELECT_PLAN'; payload: TestPlan }
  | { type: 'UPDATE_FILTERS'; payload: Partial<FilterParams> };

const testPlanReducer = (state: TestPlanState, action: TestPlanAction): TestPlanState => {
  switch (action.type) {
    case 'SET_LOADING':
      return { ...state, loading: action.payload };
    case 'SET_PLANS':
      return { ...state, plans: action.payload };
    // ... 其他case
    default:
      return state;
  }
};

性能优化

1. 使用React.memo

// ✅ 避免不必要的重渲染
const TestCaseItem = React.memo<TestCaseItemProps>(({ 
  testCase, 
  onEdit, 
  onDelete 
}) => {
  return (
    <div>
      {/* 组件内容 */}
    </div>
  );
});

2. 使用useMemo和useCallback

const TestCaseList = ({ dataSource, filters }) => {
  // ✅ 缓存计算结果
  const filteredData = useMemo(() => {
    return dataSource.filter(item => 
      item.case_func.includes(filters.keyword)
    );
  }, [dataSource, filters.keyword]);
  
  // ✅ 缓存回调函数
  const handleEdit = useCallback((record: TestCase) => {
    // 编辑逻辑
  }, []);
  
  return (
    <ProTable
      dataSource={filteredData}
      onEdit={handleEdit}
    />
  );
};

3. 代码分割和懒加载

// ✅ 路由级别的代码分割
const TestCaseList = lazy(() => import('./TestCaseList'));
const TestSuiteList = lazy(() => import('./TestSuiteList'));

// 在路由配置中使用
{
  path: '/test/case',
  component: () => (
    <Suspense fallback={<PageLoading />}>
      <TestCaseList />
    </Suspense>
  ),
}

错误处理

1. 错误边界

class ErrorBoundary extends React.Component<
  { children: React.ReactNode },
  { hasError: boolean }
> {
  constructor(props: any) {
    super(props);
    this.state = { hasError: false };
  }
  
  static getDerivedStateFromError(error: Error) {
    return { hasError: true };
  }
  
  componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
    console.error('Error caught by boundary:', error, errorInfo);
  }
  
  render() {
    if (this.state.hasError) {
      return <div>Something went wrong.</div>;
    }
    
    return this.props.children;
  }
}

2. API错误处理

// ✅ 统一的错误处理
const useApiCall = <T>(apiFunction: (...args: any[]) => Promise<T>) => {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  
  const execute = useCallback(async (...args: any[]) => {
    setLoading(true);
    setError(null);
    
    try {
      const result = await apiFunction(...args);
      return result;
    } catch (err) {
      const errorMessage = err instanceof Error ? err.message : '未知错误';
      setError(errorMessage);
      message.error(errorMessage);
      throw err;
    } finally {
      setLoading(false);
    }
  }, [apiFunction]);
  
  return { execute, loading, error };
};

🔧 后端开发最佳实践

API设计原则

1. RESTful设计

# ✅ 符合RESTful规范的API设计
@app.route('/api/test_cases', methods=['GET'])
def get_test_cases():
    """获取测试用例列表"""
    pass

@app.route('/api/test_cases', methods=['POST'])
def create_test_case():
    """创建测试用例"""
    pass

@app.route('/api/test_cases/<int:case_id>', methods=['PUT'])
def update_test_case(case_id):
    """更新测试用例"""
    pass

@app.route('/api/test_cases/<int:case_id>', methods=['DELETE'])
def delete_test_case(case_id):
    """删除测试用例"""
    pass

2. 统一响应格式

from typing import Any, Optional
from dataclasses import dataclass

@dataclass
class ApiResponse:
    success: bool
    data: Any = None
    message: str = ""
    code: int = 200

def success_response(data: Any = None, message: str = "操作成功") -> dict:
    """成功响应"""
    return ApiResponse(
        success=True,
        data=data,
        message=message
    ).__dict__

def error_response(message: str, code: int = 400, data: Any = None) -> dict:
    """错误响应"""
    return ApiResponse(
        success=False,
        data=data,
        message=message,
        code=code
    ).__dict__

数据库操作最佳实践

1. 使用ORM

# ✅ 使用SQLAlchemy ORM
class TestCase(db.Model):
    __tablename__ = 'test_cases'
    
    id = db.Column(db.Integer, primary_key=True)
    case_func = db.Column(db.String(255), nullable=False)
    case_desc = db.Column(db.Text)
    module_id = db.Column(db.Integer, db.ForeignKey('test_modules.id'))
    created_at = db.Column(db.DateTime, default=datetime.utcnow)
    updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
    
    # 关系定义
    module = db.relationship('TestModule', backref='test_cases')
    
    def to_dict(self):
        """转换为字典"""
        return {
            'id': self.id,
            'case_func': self.case_func,
            'case_desc': self.case_desc,
            'module_id': self.module_id,
            'created_at': self.created_at.isoformat(),
            'updated_at': self.updated_at.isoformat()
        }

2. 数据库事务

from contextlib import contextmanager

@contextmanager
def db_transaction():
    """数据库事务上下文管理器"""
    try:
        db.session.begin()
        yield db.session
        db.session.commit()
    except Exception as e:
        db.session.rollback()
        raise e
    finally:
        db.session.close()

# 使用示例
def create_test_suite_with_cases(suite_data, case_ids):
    with db_transaction() as session:
        # 创建测试套件
        suite = TestSuite(**suite_data)
        session.add(suite)
        session.flush()  # 获取ID
        
        # 关联测试用例
        for case_id in case_ids:
            association = SuiteCaseAssociation(
                suite_id=suite.id,
                case_id=case_id
            )
            session.add(association)

3. 查询优化

# ✅ 使用预加载避免N+1查询
def get_test_suites_with_cases():
    return TestSuite.query.options(
        joinedload(TestSuite.test_cases)
    ).all()

# ✅ 使用索引优化查询
class TestCase(db.Model):
    # 添加索引
    __table_args__ = (
        db.Index('idx_case_func', 'case_func'),
        db.Index('idx_module_id', 'module_id'),
        db.Index('idx_created_at', 'created_at'),
    )

异步任务最佳实践

1. Celery任务设计

from celery import Celery
from celery.utils.log import get_task_logger

logger = get_task_logger(__name__)

@celery.task(bind=True, max_retries=3)
def execute_test_suite(self, suite_id: int, env: str):
    """执行测试套件"""
    try:
        logger.info(f"开始执行测试套件 {suite_id}")
        
        # 更新任务状态
        self.update_state(
            state='PROGRESS',
            meta={'current': 0, 'total': 100, 'status': '准备中...'}
        )
        
        # 执行测试逻辑
        result = run_test_suite(suite_id, env)
        
        logger.info(f"测试套件 {suite_id} 执行完成")
        return result
        
    except Exception as exc:
        logger.error(f"测试套件 {suite_id} 执行失败: {exc}")
        
        # 重试逻辑
        if self.request.retries < self.max_retries:
            raise self.retry(countdown=60, exc=exc)
        else:
            # 记录最终失败
            record_test_failure(suite_id, str(exc))
            raise exc

2. 任务监控

@celery.task
def monitor_test_execution(task_id: str):
    """监控测试执行状态"""
    result = celery.AsyncResult(task_id)
    
    if result.state == 'PENDING':
        response = {
            'state': result.state,
            'status': '等待执行...'
        }
    elif result.state == 'PROGRESS':
        response = {
            'state': result.state,
            'current': result.info.get('current', 0),
            'total': result.info.get('total', 1),
            'status': result.info.get('status', '')
        }
    elif result.state == 'SUCCESS':
        response = {
            'state': result.state,
            'result': result.result
        }
    else:
        response = {
            'state': result.state,
            'error': str(result.info)
        }
    
    return response

安全最佳实践

1. 输入验证

from marshmallow import Schema, fields, validate

class TestCaseSchema(Schema):
    case_func = fields.Str(required=True, validate=validate.Length(min=1, max=255))
    case_desc = fields.Str(validate=validate.Length(max=1000))
    module_id = fields.Int(required=True, validate=validate.Range(min=1))
    tags = fields.List(fields.Str())

def create_test_case():
    schema = TestCaseSchema()
    try:
        data = schema.load(request.json)
    except ValidationError as err:
        return error_response("参数验证失败", data=err.messages)
    
    # 创建测试用例逻辑
    pass

2. 权限控制

from functools import wraps
from flask_jwt_extended import get_jwt_identity, verify_jwt_in_request

def require_permission(permission: str):
    """权限装饰器"""
    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            verify_jwt_in_request()
            current_user_id = get_jwt_identity()
            
            if not has_permission(current_user_id, permission):
                return error_response("权限不足", code=403)
            
            return f(*args, **kwargs)
        return decorated_function
    return decorator

@app.route('/api/test_cases', methods=['POST'])
@require_permission('test_case:create')
def create_test_case():
    pass

🧪 测试最佳实践

单元测试

import pytest
from unittest.mock import Mock, patch

class TestTestCaseService:
    def setup_method(self):
        """每个测试方法前执行"""
        self.service = TestCaseService()
    
    def test_create_test_case_success(self):
        """测试创建测试用例成功"""
        # Arrange
        case_data = {
            'case_func': 'test_login',
            'case_desc': '测试登录功能',
            'module_id': 1
        }
        
        # Act
        result = self.service.create_test_case(case_data)
        
        # Assert
        assert result.success is True
        assert result.data['case_func'] == 'test_login'
    
    @patch('app.services.db.session.commit')
    def test_create_test_case_db_error(self, mock_commit):
        """测试数据库错误情况"""
        # Arrange
        mock_commit.side_effect = Exception("Database error")
        case_data = {'case_func': 'test_login'}
        
        # Act & Assert
        with pytest.raises(Exception):
            self.service.create_test_case(case_data)

集成测试

class TestTestCaseAPI:
    def test_create_test_case_api(self, client, auth_headers):
        """测试创建测试用例API"""
        data = {
            'case_func': 'test_api_login',
            'case_desc': '测试API登录',
            'module_id': 1
        }
        
        response = client.post(
            '/api/test_cases',
            json=data,
            headers=auth_headers
        )
        
        assert response.status_code == 200
        assert response.json['success'] is True
        assert response.json['data']['case_func'] == 'test_api_login'

📊 代码质量

代码审查清单

  • 代码是否遵循项目编码规范
  • 是否有适当的错误处理
  • 是否有必要的单元测试
  • 是否有性能问题
  • 是否有安全漏洞
  • 是否有适当的注释和文档

静态代码分析

# Python代码质量检查
flake8 app/
black --check app/
mypy app/

# JavaScript/TypeScript代码质量检查
eslint src/
prettier --check src/
tsc --noEmit

代码覆盖率

# Python测试覆盖率
pytest --cov=app --cov-report=html

# JavaScript测试覆盖率
npm run test:coverage

💡 性能优化

数据库优化

  1. 合理使用索引: 为常用查询字段添加索引
  2. 避免N+1查询: 使用预加载或批量查询
  3. 分页查询: 大数据量时使用分页
  4. 连接池: 配置合适的数据库连接池

缓存策略

from flask_caching import Cache

cache = Cache()

@cache.memoize(timeout=300)
def get_test_cases_by_module(module_id):
    """缓存测试用例查询结果"""
    return TestCase.query.filter_by(module_id=module_id).all()

# 缓存失效
def update_test_case(case_id, data):
    case = TestCase.query.get(case_id)
    # 更新逻辑
    
    # 清除相关缓存
    cache.delete_memoized(get_test_cases_by_module, case.module_id)

前端性能优化

  1. 代码分割: 按路由或功能分割代码
  2. 懒加载: 延迟加载非关键资源
  3. 缓存策略: 合理使用浏览器缓存
  4. 图片优化: 使用适当的图片格式和大小

🎉 最佳实践大师! 你已经掌握了开发的精髓。记住,最佳实践不是教条,而是经验的总结。在实际开发中要灵活运用,持续改进!