四十、【高级特性篇】接口用例数据驱动:引入随机变量与动态数据生成
四十、【高级特性篇】接口用例数据驱动:引入随机变量与动态数据生成
-
- 前言
-
- 准备工作
- 第一部分:后端数据模型与 API 调整
-
- 1. 升级 `TestCase` 模型
- 2. 生成并应用数据库迁移
- 3. 更新 `TestCaseSerializer`
- 第二部分:后端测试执行器强化
-
- 1. 修改 `execute_api_test_case` 函数
- 2. Celery 任务 `execute_test_plan_task` 保持不变
- 第三部分:前端交互界面改造
-
- 1. 更新 `api/testcase.ts` 类型定义
- 2. 大改 `TestCaseEditView.vue`
- 第四部分:全面测试与验证
- 总结
前言
一个测试用例可能需要在不同的数据场景下执行,或者需要使用随机数据来模拟真实场景。例如,测试注册接口时需要生成一个不重复的用户名或手机号;测试查询接口时,可能需要一个随机的日期范围。
本文目标:
在 TestCaseEditView.vue
(测试用例编辑页面) 中,为“接口测试”类型用例添加一个“随机变量”配置区域。用户可以在此定义要生成的随机变量,包括变量名、数据类型(如字符串、数字、手机号、UUID、日期时间等)以及类型相关的配置(如字符串长度、数字范围、日期格式)。这些随机变量将通过 { {variable_name}}
格式引用在请求中,并在执行时自动生成和注入。
准备工作
- 前端项目就绪:
test-platform/frontend
项目可以正常运行 (npm run dev
)。 - 后端 API 运行中: Django 后端服务运行。
TestCase
模型已存在并支持extract_params
字段。requests
库和jsonpath-ng
已安装在后端虚拟环境。- Celery 和 Redis 已配置并运行。
- Element Plus 集成完毕。
第一部分:后端数据模型与 API 调整
1. 升级 TestCase
模型
打开 test-platform/api/models.py
。
在 TestCase
模型中,新增 random_variables
字段。
# test-platform/api/models.py
# ... (其他导入和模型定义) ...class TestCase(BaseModel):# ... (原有字段,如 module, priority, case_type, request_method, request_url, assertions, extract_params 等) ...# --- 新增:随机变量生成规则 ---random_variables = models.TextField(null=True, blank=True, default='[]', verbose_name="随机变量生成规则 (JSON格式)")# ...
2. 生成并应用数据库迁移
# 在 test-platform 目录下
python manage.py makemigrations api
python manage.py migrate api
3. 更新 TestCaseSerializer
打开 test-platform/api/serializers.py
,将新的 random_variables
字段添加到 TestCaseSerializer
的 fields
列表中。
# test-platform/api/serializers.py
# ... (其他导入和 Serializer) ...class TestCaseSerializer(serializers.ModelSerializer):# ... (原有字段定义) ...class Meta:model = TestCasefields = ['id', 'name', 'description', 'module', 'module_name', 'project_id', 'project_name','priority', 'priority_display','request_method', 'request_url', 'request_headers', 'request_body', 'assertions', 'extract_params', 'random_variables', # 新增字段'precondition', 'steps_text', 'expected_result','case_type', 'case_type_display', 'maintainer','create_time', 'update_time']# ... (extra_kwargs 保持不变) ...
第二部分:后端测试执行器强化
改造测试执行器,使其支持随机变量的生成和注入。
1. 修改 execute_api_test_case
函数
打开 test-platform/api/services/test_executor.py
,新增随机变量生成逻辑。
# test-platform/api/services/test_executor.py
import requests
import json
import time
import urllib.parse
import re
import random
import string
from datetime import datetime, timedelta
from typing import Dict, List, Any, Tuple, Optionalfrom ..models import TestCase, Environment # 确保导入 TestCase, Environment# ... (断言类型和 jsonpath_ng 导入保持不变) ...def generate_random_value(rule: Dict[str, Any]) -> Any:"""根据规则生成随机值"""var_type = rule.get('type')config = rule.get('config', {})if var_type == 'string':length = config.get('length', 8)chars = string.ascii_letters + string.digitsreturn ''.join(random.choice(chars) for _ in range(length))elif var_type == 'int':min_val = config.get('min', 0)max_val = config.get('max', 100)return random.randint(min_val, max_val)elif var_type == 'float':min_val = config.get('min', 0.0)max_val = config.get('max', 100.0)decimal_places = config.get('decimal_places', 2)return round(random.uniform(min_val, max_val), decimal_places)elif var_type == 'phone': # 简单模拟中国大陆手机号prefix = random.choice(['13', '15', '17', '18', '19']) # 更多号段可添加suffix = ''.join(random.choice(string.digits) for _ in range(9))return f"{prefix}{suffix}"elif var_type == 'email':username_len = config.get('username_length', 8)domain = config.get('domain', 'example.com')username_chars = string.ascii_letters + string.digitsusername = ''.join(random.choice(username_chars) for _ in range(username_len))return f"{username}@{domain}"elif var_type == 'uuid':import uuid # 导入uuid模块return str(uuid.uuid4())elif var_type == 'datetime':# 默认生成当前附近时间,格式可配置format_str = config.get('format', '%Y-%m-%d %H:%M:%S')start_date = datetime.now() - timedelta(days=config.get('days_ago', 30))end_date = datetime.now() + timedelta(days=config.get('days_ahead', 30))random_seconds = random.randint(0, int((end_date - start_date).total_seconds()))random_datetime = start_date + timedelta(seconds=random_seconds)return random_datetime.strftime(format_str)elif var_type == 'date':format_str = config.get('format', '%Y-%m-%d')start_date = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0) - timedelta(days=config.get('days_ago', 30))end_date = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0) + timedelta(days=config.get('days_ahead', 30))random_days = random.randint(0, (end_date - start_date).days)random_date = start_date + timedelta(days=random_days)return random_date.strftime(format_str)# 可以添加更多类型,例如 IP 地址、身份证号等return Nonedef execute_api_test_case(test_case: TestCase,environment: Optional[Environment] = None,context_variables: Optional[Dict[str, Any]] = None
) -> Dict[str, Any]:"""执行单个 API 测试用例并返回结果字典。可以传入 environment 对象,用于动态替换 base_url 和添加公共请求头。context_variables: 上下文变量字典,用于参数注入和提取"""if context_variables is None:context_variables = {}result = {"status": "ERROR","request_data": {},"response_data": {},"assertion_results": [],"error_message": None,"duration": 0.0,"context_variables": context_variables # 返回更新后的上下文变量}start_time = time.time(