feat(admin): 添加用户管理相关文件

添加用户管理视图、API和状态管理文件
This commit is contained in:
张翔
2026-03-28 14:37:29 +08:00
commit 08ea5fbe98
1643 changed files with 255646 additions and 0 deletions
@@ -0,0 +1,161 @@
"""CLI接口单元测试"""
import pytest
from click.testing import CliRunner
from pathlib import Path
import json
import tempfile
from apitest.cli_module import cli
@pytest.fixture
def runner():
"""创建CLI测试运行器"""
return CliRunner()
@pytest.fixture
def sample_test_cases(tmp_path):
"""创建示例测试用例文件"""
test_data = [
{
"id": "TC001",
"name": "测试用例1",
"description": "测试用例1",
"module": "test",
"endpoint": "/api/test1",
"method": "GET",
"headers": {},
"enabled": True
},
{
"id": "TC002",
"name": "测试用例2",
"description": "测试用例2",
"module": "user",
"endpoint": "/api/user",
"method": "POST",
"headers": {},
"enabled": True,
"tags": ["smoke", "regression"],
"priority": 1
}
]
test_file = tmp_path / "test_cases.json"
with open(test_file, "w", encoding="utf-8") as f:
json.dump(test_data, f)
return test_file
def test_cli_version(runner):
"""测试CLI版本命令"""
result = runner.invoke(cli, ["--version"])
assert result.exit_code == 0
assert "1.0.0" in result.output
def test_cli_help(runner):
"""测试CLI帮助命令"""
result = runner.invoke(cli, ["--help"])
assert result.exit_code == 0
assert "黑盒API测试工具" in result.output
def test_list_command(runner, sample_test_cases):
"""测试列出测试用例命令"""
result = runner.invoke(cli, ["list", str(sample_test_cases)])
assert result.exit_code == 0
assert "测试用例总数: 2" in result.output
assert "TC001" in result.output
assert "TC002" in result.output
def test_list_command_with_module_filter(runner, sample_test_cases):
"""测试列出测试用例命令(模块过滤)"""
result = runner.invoke(cli, ["list", str(sample_test_cases), "--module", "test"])
assert result.exit_code == 0
assert "TC001" in result.output
assert "TC002" not in result.output
def test_list_command_with_tag_filter(runner, sample_test_cases):
"""测试列出测试用例命令(标签过滤)"""
result = runner.invoke(cli, ["list", str(sample_test_cases), "--tag", "smoke"])
assert result.exit_code == 0
assert "TC001" not in result.output
assert "TC002" in result.output
def test_list_command_with_priority_filter(runner, sample_test_cases):
"""测试列出测试用例命令(优先级过滤)"""
result = runner.invoke(cli, ["list", str(sample_test_cases), "--priority", "1"])
assert result.exit_code == 0
assert "TC001" not in result.output
assert "TC002" in result.output
def test_validate_command(runner, sample_test_cases):
"""测试验证测试用例命令"""
result = runner.invoke(cli, ["validate", str(sample_test_cases)])
assert result.exit_code == 0
assert "验证通过" in result.output
def test_validate_command_invalid_file(runner, tmp_path):
"""测试验证测试用例命令(无效文件)"""
invalid_file = tmp_path / "invalid.json"
with open(invalid_file, "w", encoding="utf-8") as f:
f.write("{ invalid json }")
result = runner.invoke(cli, ["validate", str(invalid_file)])
assert result.exit_code != 0
def test_config_command(runner):
"""测试配置命令"""
result = runner.invoke(cli, ["config"])
assert result.exit_code == 0
assert "当前配置" in result.output
def test_config_command_with_key(runner):
"""测试配置命令(指定键)"""
result = runner.invoke(cli, ["config", "--key", "target.base_url"])
assert result.exit_code == 0
def test_run_command_no_test_cases(runner):
"""测试运行命令(无测试用例)"""
result = runner.invoke(cli, ["run"])
assert result.exit_code != 0
assert "请指定测试用例文件路径" in result.output
def test_run_command_help(runner):
"""测试运行命令帮助"""
result = runner.invoke(cli, ["run", "--help"])
assert result.exit_code == 0
assert "运行测试用例" in result.output
def test_list_command_help(runner):
"""测试列出命令帮助"""
result = runner.invoke(cli, ["list", "--help"])
assert result.exit_code == 0
assert "列出测试用例" in result.output
def test_validate_command_help(runner):
"""测试验证命令帮助"""
result = runner.invoke(cli, ["validate", "--help"])
assert result.exit_code == 0
assert "验证测试用例文件" in result.output
def test_config_command_help(runner):
"""测试配置命令帮助"""
result = runner.invoke(cli, ["config", "--help"])
assert result.exit_code == 0
assert "查看配置" in result.output
@@ -0,0 +1,224 @@
import pytest
import yaml
import os
from pathlib import Path
from apitest.config.config_manager import ConfigManager
from apitest.models.exceptions import ConfigException
class TestConfigManager:
"""测试ConfigManager配置管理器"""
@pytest.fixture
def temp_config_file(self, tmp_path):
"""创建临时配置文件"""
config_data = {
"target": {
"base_url": "http://localhost:8080",
"timeout": 5000,
"max_retries": 3
},
"auth": {
"username": "test_user",
"password": "test_pass",
"login_endpoint": "/sys/auth/login"
},
"test": {
"data_dir": "data",
"test_cases_dir": "test_cases",
"parallel": True,
"parallel_threads": 4,
"retry_count": 2,
"stop_on_failure": False,
"max_response_time": 5000
},
"report": {
"output_dir": "reports",
"format": "html"
},
"logging": {
"level": "INFO",
"format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
"file": "logs/api_test.log"
}
}
config_file = tmp_path / "config.yaml"
with open(config_file, "w", encoding="utf-8") as f:
yaml.dump(config_data, f)
return config_file
def test_config_manager_initialization(self, temp_config_file):
"""测试配置管理器初始化"""
config_manager = ConfigManager(str(temp_config_file))
assert config_manager.config_path == temp_config_file
assert config_manager._config is not None
def test_config_manager_nonexistent_file(self):
"""测试配置文件不存在"""
with pytest.raises(ConfigException, match="配置文件不存在"):
ConfigManager("/nonexistent/config.yaml")
def test_get_config_value(self, temp_config_file):
"""测试获取配置值"""
config_manager = ConfigManager(str(temp_config_file))
assert config_manager.get("target.base_url") == "http://localhost:8080"
assert config_manager.get("target.timeout") == 5000
assert config_manager.get("target.max_retries") == 3
def test_get_nested_config_value(self, temp_config_file):
"""测试获取嵌套配置值"""
config_manager = ConfigManager(str(temp_config_file))
assert config_manager.get("auth.username") == "test_user"
assert config_manager.get("auth.password") == "test_pass"
assert config_manager.get("auth.login_endpoint") == "/sys/auth/login"
def test_get_config_value_with_default(self, temp_config_file):
"""测试获取配置值(带默认值)"""
config_manager = ConfigManager(str(temp_config_file))
assert config_manager.get("nonexistent.key", "default_value") == "default_value"
assert config_manager.get("target.nonexistent", 0) == 0
def test_get_target_config(self, temp_config_file):
"""测试获取目标系统配置"""
config_manager = ConfigManager(str(temp_config_file))
target_config = config_manager.get_target_config()
assert target_config["base_url"] == "http://localhost:8080"
assert target_config["timeout"] == 5000
assert target_config["max_retries"] == 3
def test_get_auth_config(self, temp_config_file):
"""测试获取认证配置"""
config_manager = ConfigManager(str(temp_config_file))
auth_config = config_manager.get_auth_config()
assert auth_config["username"] == "test_user"
assert auth_config["password"] == "test_pass"
assert auth_config["login_endpoint"] == "/sys/auth/login"
def test_get_test_config(self, temp_config_file):
"""测试获取测试配置"""
config_manager = ConfigManager(str(temp_config_file))
test_config = config_manager.get_test_config()
assert test_config["data_dir"] == "data"
assert test_config["test_cases_dir"] == "test_cases"
assert test_config["parallel"] == True
assert test_config["parallel_threads"] == 4
def test_get_report_config(self, temp_config_file):
"""测试获取报告配置"""
config_manager = ConfigManager(str(temp_config_file))
report_config = config_manager.get_report_config()
assert report_config["output_dir"] == "reports"
assert report_config["format"] == "html"
def test_get_logging_config(self, temp_config_file):
"""测试获取日志配置"""
config_manager = ConfigManager(str(temp_config_file))
logging_config = config_manager.get_logging_config()
assert logging_config["level"] == "INFO"
assert logging_config["file"] == "logs/api_test.log"
def test_get_base_url(self, temp_config_file):
"""测试获取基础URL"""
config_manager = ConfigManager(str(temp_config_file))
assert config_manager.get_base_url() == "http://localhost:8080"
def test_get_timeout(self, temp_config_file):
"""测试获取超时时间"""
config_manager = ConfigManager(str(temp_config_file))
assert config_manager.get_timeout() == 5000
def test_get_max_retries(self, temp_config_file):
"""测试获取最大重试次数"""
config_manager = ConfigManager(str(temp_config_file))
assert config_manager.get_max_retries() == 3
def test_get_login_endpoint(self, temp_config_file):
"""测试获取登录端点"""
config_manager = ConfigManager(str(temp_config_file))
assert config_manager.get_login_endpoint() == "/sys/auth/login"
def test_is_parallel_enabled(self, temp_config_file):
"""测试是否启用并行执行"""
config_manager = ConfigManager(str(temp_config_file))
assert config_manager.is_parallel_enabled() == True
def test_get_parallel_threads(self, temp_config_file):
"""测试获取并行线程数"""
config_manager = ConfigManager(str(temp_config_file))
assert config_manager.get_parallel_threads() == 4
def test_get_retry_count(self, temp_config_file):
"""测试获取重试次数"""
config_manager = ConfigManager(str(temp_config_file))
assert config_manager.get_retry_count() == 2
def test_should_stop_on_failure(self, temp_config_file):
"""测试是否在失败时停止"""
config_manager = ConfigManager(str(temp_config_file))
assert config_manager.should_stop_on_failure() == False
def test_get_max_response_time(self, temp_config_file):
"""测试获取最大响应时间"""
config_manager = ConfigManager(str(temp_config_file))
assert config_manager.get_max_response_time() == 5000
def test_get_report_format(self, temp_config_file):
"""测试获取报告格式"""
config_manager = ConfigManager(str(temp_config_file))
assert config_manager.get_report_format() == "html"
def test_get_log_level(self, temp_config_file):
"""测试获取日志级别"""
config_manager = ConfigManager(str(temp_config_file))
assert config_manager.get_log_level() == "INFO"
def test_get_log_format(self, temp_config_file):
"""测试获取日志格式"""
config_manager = ConfigManager(str(temp_config_file))
expected_format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
assert config_manager.get_log_format() == expected_format
def test_get_log_file(self, temp_config_file):
"""测试获取日志文件路径"""
config_manager = ConfigManager(str(temp_config_file))
log_file = config_manager.get_log_file()
assert log_file is not None
assert log_file.name == "api_test.log"
def test_get_auth_credentials_with_env(self, temp_config_file, monkeypatch):
"""测试获取认证凭据(环境变量)"""
monkeypatch.setenv("TEST_USERNAME", "env_user")
monkeypatch.setenv("TEST_PASSWORD", "env_pass")
config_manager = ConfigManager(str(temp_config_file))
credentials = config_manager.get_auth_credentials()
assert credentials["username"] == "env_user"
assert credentials["password"] == "env_pass"
def test_reload_config(self, temp_config_file):
"""测试重新加载配置"""
config_manager = ConfigManager(str(temp_config_file))
original_url = config_manager.get_base_url()
assert original_url == "http://localhost:8080"
with open(temp_config_file, "r+", encoding="utf-8") as f:
config_data = yaml.safe_load(f)
config_data["target"]["base_url"] = "http://new-url:8080"
f.seek(0)
yaml.dump(config_data, f)
f.truncate()
config_manager.reload()
assert config_manager.get_base_url() == "http://new-url:8080"
@@ -0,0 +1,137 @@
import pytest
import logging
import sys
from pathlib import Path
from unittest.mock import patch, MagicMock
from apitest.config.config_manager import ConfigManager
from apitest.config.logger_manager import LoggerManager
class TestLoggerManager:
"""测试LoggerManager日志管理器"""
@pytest.fixture
def mock_config_manager(self):
"""模拟配置管理器"""
config_manager = MagicMock(spec=ConfigManager)
config_manager.get_log_level.return_value = "INFO"
config_manager.get_log_format.return_value = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
config_manager.get_log_file.return_value = None
return config_manager
@pytest.fixture
def logger_manager(self, mock_config_manager):
"""创建日志管理器实例"""
return LoggerManager(mock_config_manager)
def test_logger_manager_initialization(self, mock_config_manager):
"""测试日志管理器初始化"""
logger_manager = LoggerManager(mock_config_manager)
assert logger_manager.config_manager == mock_config_manager
assert isinstance(logger_manager._loggers, dict)
@pytest.fixture
def mock_config_manager_with_file(self, tmp_path):
"""模拟带日志文件的配置管理器"""
config_manager = MagicMock(spec=ConfigManager)
config_manager.get_log_level.return_value = "DEBUG"
config_manager.get_log_format.return_value = "%(name)s - %(levelname)s - %(message)s"
config_manager.get_log_file.return_value = tmp_path / "test.log"
return config_manager
def test_logger_manager_with_file(self, mock_config_manager_with_file):
"""测试带日志文件的日志管理器初始化"""
logger_manager = LoggerManager(mock_config_manager_with_file)
assert logger_manager.config_manager == mock_config_manager_with_file
def test_get_logger(self, logger_manager):
"""测试获取日志记录器"""
logger = logger_manager.get_logger("test_logger")
assert isinstance(logger, logging.Logger)
assert logger.name == "test_logger"
assert "test_logger" in logger_manager._loggers
def test_get_logger_cached(self, logger_manager):
"""测试获取缓存的日志记录器"""
logger1 = logger_manager.get_logger("test_logger")
logger2 = logger_manager.get_logger("test_logger")
assert logger1 is logger2
def test_set_level(self, logger_manager):
"""测试设置日志级别"""
logger_manager.set_level("DEBUG")
root_logger = logging.getLogger()
assert root_logger.level == logging.DEBUG
def test_add_file_handler(self, logger_manager, tmp_path):
"""测试添加文件处理器"""
log_file = tmp_path / "test_add_handler.log"
initial_handler_count = len([h for h in logging.getLogger().handlers if isinstance(h, logging.FileHandler)])
logger_manager.add_file_handler(log_file)
root_logger = logging.getLogger()
file_handlers = [h for h in root_logger.handlers if isinstance(h, logging.FileHandler)]
assert len(file_handlers) > initial_handler_count
new_handlers = file_handlers[initial_handler_count:]
assert len(new_handlers) > 0
assert str(log_file) in new_handlers[0].baseFilename
def test_add_file_handler_with_level(self, logger_manager, tmp_path):
"""测试添加带级别的文件处理器"""
log_file = tmp_path / "test_add_handler_level.log"
initial_handler_count = len([h for h in logging.getLogger().handlers if isinstance(h, logging.FileHandler)])
logger_manager.add_file_handler(log_file, "ERROR")
root_logger = logging.getLogger()
file_handlers = [h for h in root_logger.handlers if isinstance(h, logging.FileHandler)]
assert len(file_handlers) > initial_handler_count
new_handlers = file_handlers[initial_handler_count:]
assert len(new_handlers) > 0
assert new_handlers[0].level == logging.ERROR
def test_remove_all_handlers(self, logger_manager):
"""测试移除所有处理器"""
original_handler_count = len(logging.getLogger().handlers)
logger_manager.remove_all_handlers()
assert len(logging.getLogger().handlers) == 0
@patch('apitest.config.logger_manager.setup_logger')
def test_setup_logger_function(self, mock_setup):
"""测试setup_logger函数"""
from apitest.config.logger_manager import setup_logger
mock_config = MagicMock(spec=ConfigManager)
setup_logger(mock_config)
mock_setup.assert_called_once_with(mock_config)
class TestSetupLoggerIntegration:
"""测试setup_logger集成"""
def test_setup_logger_creates_logger_manager(self):
"""测试setup_logger创建日志管理器"""
config_data = {
"logging": {
"level": "INFO",
"format": "%(name)s - %(levelname)s - %(message)s",
"file": None
}
}
with patch('apitest.config.config_manager.ConfigManager') as mock_config_class:
mock_config = MagicMock(spec=ConfigManager)
mock_config.get_log_level.return_value = "INFO"
mock_config.get_log_format.return_value = "%(name)s - %(levelname)s - %(message)s"
mock_config.get_log_file.return_value = None
mock_config_class.return_value = mock_config
from apitest.config.logger_manager import setup_logger
logger_manager = setup_logger(mock_config)
assert isinstance(logger_manager, LoggerManager)
assert logger_manager.config_manager == mock_config
@@ -0,0 +1,378 @@
import pytest
from datetime import datetime
from apitest.models.test_models import (
HTTPMethod,
ValidationRule,
TestCase,
PerformanceMetrics,
TestResult,
TestSuiteResult
)
from apitest.models.exceptions import (
APITestException,
ConfigException,
DataException,
AuthException,
RequestException,
ValidationException,
TestRunException,
ReportException
)
class TestHTTPMethod:
"""测试HTTPMethod枚举"""
def test_http_methods(self):
"""测试所有HTTP方法"""
assert HTTPMethod.GET.value == "GET"
assert HTTPMethod.POST.value == "POST"
assert HTTPMethod.PUT.value == "PUT"
assert HTTPMethod.DELETE.value == "DELETE"
assert HTTPMethod.PATCH.value == "PATCH"
assert HTTPMethod.HEAD.value == "HEAD"
assert HTTPMethod.OPTIONS.value == "OPTIONS"
class TestValidationRule:
"""测试ValidationRule数据类"""
def test_validation_rule_creation(self):
"""测试创建验证规则"""
rule = ValidationRule(
type="status_code",
expected=200,
message="状态码应为200"
)
assert rule.type == "status_code"
assert rule.expected == 200
assert rule.message == "状态码应为200"
assert rule.json_path is None
def test_validation_rule_with_json_path(self):
"""测试带JSON路径的验证规则"""
rule = ValidationRule(
type="json_path",
expected="success",
json_path="$.status",
message="状态应为success"
)
assert rule.json_path == "$.status"
def test_validation_rule_immutability(self):
"""测试验证规则的不可变性"""
rule = ValidationRule(type="status_code", expected=200)
with pytest.raises(Exception):
rule.expected = 201
class TestTestCaseModel:
"""测试TestCase数据类"""
def test_test_case_creation(self):
"""测试创建测试用例"""
test_case = TestCase(
id="TC001",
name="测试用户登录",
description="验证用户登录功能",
module="user",
endpoint="/api/auth/login",
method=HTTPMethod.POST,
headers={"Content-Type": "application/json"},
body={"username": "admin", "password": "admin123"}
)
assert test_case.id == "TC001"
assert test_case.name == "测试用户登录"
assert test_case.method == HTTPMethod.POST
assert test_case.auth_required == True
assert test_case.enabled == True
assert test_case.dependencies == []
assert test_case.validations == []
assert test_case.tags == []
def test_test_case_with_dependencies(self):
"""测试带依赖的测试用例"""
test_case = TestCase(
id="TC002",
name="测试获取用户信息",
description="验证获取用户信息功能",
module="user",
endpoint="/api/user/info",
method=HTTPMethod.GET,
headers={"Content-Type": "application/json"},
dependencies=["TC001"]
)
assert len(test_case.dependencies) == 1
assert "TC001" in test_case.dependencies
def test_test_case_with_validations(self):
"""测试带验证规则的测试用例"""
test_case = TestCase(
id="TC003",
name="测试创建用户",
description="验证创建用户功能",
module="user",
endpoint="/api/user",
method=HTTPMethod.POST,
headers={"Content-Type": "application/json"},
body={"username": "test", "password": "test123"},
validations=[
{"type": "status_code", "expected": 201},
{"type": "json_path", "json_path": "$.success", "expected": True}
]
)
assert len(test_case.validations) == 2
assert test_case.validations[0]["type"] == "status_code"
def test_test_case_immutability(self):
"""测试测试用例的不可变性"""
test_case = TestCase(
id="TC004",
name="测试用例",
description="测试",
module="test",
endpoint="/api/test",
method=HTTPMethod.GET,
headers={}
)
with pytest.raises(Exception):
test_case.name = "修改后的名称"
class TestPerformanceMetrics:
"""测试PerformanceMetrics数据类"""
def test_performance_metrics_creation(self):
"""测试创建性能指标"""
metrics = PerformanceMetrics(
response_time=500,
request_size=100,
response_size=200,
timestamp=datetime.now()
)
assert metrics.response_time == 500
assert metrics.request_size == 100
assert metrics.response_size == 200
def test_performance_metrics_to_dict(self):
"""测试性能指标转换为字典"""
timestamp = datetime.now()
metrics = PerformanceMetrics(
response_time=500,
request_size=100,
response_size=200,
timestamp=timestamp
)
result = metrics.to_dict()
assert result["response_time"] == 500
assert result["request_size"] == 100
assert result["response_size"] == 200
assert result["timestamp"] == timestamp.isoformat()
class TestTestResultModel:
"""测试TestResult数据类"""
def test_test_result_creation(self):
"""测试创建测试结果"""
test_case = TestCase(
id="TC001",
name="测试用例",
description="测试",
module="test",
endpoint="/api/test",
method=HTTPMethod.GET,
headers={}
)
result = TestResult(
test_case=test_case,
passed=True,
status_code=200,
response_body={"success": True},
response_headers={"Content-Type": "application/json"}
)
assert result.passed == True
assert result.status_code == 200
assert result.execution_time == 0.0
assert result.retry_count == 0
assert result.timestamp is not None
def test_test_result_with_performance(self):
"""测试带性能指标的测试结果"""
test_case = TestCase(
id="TC001",
name="测试用例",
description="测试",
module="test",
endpoint="/api/test",
method=HTTPMethod.GET,
headers={}
)
performance = PerformanceMetrics(
response_time=500,
request_size=100,
response_size=200,
timestamp=datetime.now()
)
result = TestResult(
test_case=test_case,
passed=True,
status_code=200,
response_body={"success": True},
response_headers={},
performance=performance,
execution_time=0.5
)
assert result.performance == performance
assert result.execution_time == 0.5
def test_test_result_to_dict(self):
"""测试测试结果转换为字典"""
test_case = TestCase(
id="TC001",
name="测试用例",
description="测试",
module="test",
endpoint="/api/test",
method=HTTPMethod.GET,
headers={}
)
result = TestResult(
test_case=test_case,
passed=True,
status_code=200,
response_body={"success": True},
response_headers={}
)
result_dict = result.to_dict()
assert result_dict["test_case_id"] == "TC001"
assert result_dict["test_case_name"] == "测试用例"
assert result_dict["passed"] == True
assert result_dict["status_code"] == 200
class TestTestSuiteResultModel:
"""测试TestSuiteResult数据类"""
def test_test_suite_result_creation(self):
"""测试创建测试套件结果"""
suite_result = TestSuiteResult(
suite_name="user",
total=10,
passed=8,
failed=1,
skipped=1,
results=[],
start_time=datetime.now()
)
assert suite_result.suite_name == "user"
assert suite_result.total == 10
assert suite_result.passed == 8
assert suite_result.failed == 1
assert suite_result.skipped == 1
assert suite_result.end_time is None
def test_test_suite_result_duration(self):
"""测试测试套件执行时长"""
start_time = datetime.now()
end_time = datetime.now()
suite_result = TestSuiteResult(
suite_name="user",
total=10,
passed=8,
failed=1,
skipped=1,
results=[],
start_time=start_time,
end_time=end_time
)
assert suite_result.duration >= 0
def test_test_suite_result_pass_rate(self):
"""测试测试套件通过率"""
suite_result = TestSuiteResult(
suite_name="user",
total=10,
passed=8,
failed=1,
skipped=1,
results=[],
start_time=datetime.now()
)
assert suite_result.pass_rate == 80.0
def test_test_suite_result_pass_rate_zero_total(self):
"""测试总数为0时的通过率"""
suite_result = TestSuiteResult(
suite_name="user",
total=0,
passed=0,
failed=0,
skipped=0,
results=[],
start_time=datetime.now()
)
assert suite_result.pass_rate == 0.0
def test_test_suite_result_to_dict(self):
"""测试测试套件结果转换为字典"""
suite_result = TestSuiteResult(
suite_name="user",
total=10,
passed=8,
failed=1,
skipped=1,
results=[],
start_time=datetime.now()
)
result_dict = suite_result.to_dict()
assert result_dict["suite_name"] == "user"
assert result_dict["total"] == 10
assert result_dict["passed"] == 8
assert result_dict["failed"] == 1
assert result_dict["skipped"] == 1
assert result_dict["pass_rate"] == 80.0
class TestExceptions:
"""测试异常类"""
def test_api_test_exception(self):
"""测试API测试基础异常"""
with pytest.raises(APITestException):
raise APITestException("测试异常")
def test_config_exception(self):
"""测试配置异常"""
with pytest.raises(ConfigException):
raise ConfigException("配置错误")
def test_data_exception(self):
"""测试数据异常"""
with pytest.raises(DataException):
raise DataException("数据错误")
def test_auth_exception(self):
"""测试认证异常"""
with pytest.raises(AuthException):
raise AuthException("认证失败")
def test_request_exception(self):
"""测试请求异常"""
with pytest.raises(RequestException):
raise RequestException("请求失败")
def test_validation_exception(self):
"""测试验证异常"""
with pytest.raises(ValidationException):
raise ValidationException("验证失败")
def test_test_execution_exception(self):
"""测试测试执行异常"""
with pytest.raises(TestRunException):
raise TestRunException("执行失败")
def test_report_exception(self):
"""测试报告生成异常"""
with pytest.raises(ReportException):
raise ReportException("报告生成失败")
@@ -0,0 +1,355 @@
"""报告管理器单元测试"""
import pytest
from pathlib import Path
from datetime import datetime
from unittest.mock import Mock, patch
from apitest.report.report_manager import ReportManager
from apitest.models.test_models import TestCase, TestResult, TestSuiteResult, HTTPMethod, PerformanceMetrics
from apitest.models.exceptions import ReportException
@pytest.fixture
def report_manager():
"""创建报告管理器实例"""
logger = Mock()
return ReportManager(logger)
@pytest.fixture
def test_suite_result():
"""创建测试套件结果"""
test_cases = [
TestCase(
id="TC001",
name="测试用例1",
description="测试用例1",
module="test",
endpoint="/api/test1",
method=HTTPMethod.GET,
headers={},
enabled=True
),
TestCase(
id="TC002",
name="测试用例2",
description="测试用例2",
module="test",
endpoint="/api/test2",
method=HTTPMethod.POST,
headers={},
enabled=True
)
]
results = [
TestResult(
test_case=test_cases[0],
passed=True,
status_code=200,
response_body={"message": "success"},
response_headers={"Content-Type": "application/json"},
performance=PerformanceMetrics(
timestamp=datetime.now(),
response_time=123,
request_size=0,
response_size=0
)
),
TestResult(
test_case=test_cases[1],
passed=False,
status_code=500,
response_body={"error": "internal error"},
response_headers={"Content-Type": "application/json"},
performance=PerformanceMetrics(
timestamp=datetime.now(),
response_time=456,
request_size=0,
response_size=0
),
error_message="服务器内部错误"
)
]
return TestSuiteResult(
suite_name="Test Suite",
total=2,
passed=1,
failed=1,
skipped=0,
results=results,
start_time=datetime.now()
)
def test_generate_html_report_success(report_manager, test_suite_result, tmp_path):
"""测试生成HTML报告(成功)"""
report_path = tmp_path / "test_report.html"
result_path = report_manager.generate_html_report(
test_suite_result,
report_path,
title="测试报告"
)
assert result_path == str(report_path)
assert report_path.exists()
content = report_path.read_text(encoding="utf-8")
assert "测试报告" in content
assert "Test Suite" in content
assert "TC001" in content
assert "TC002" in content
assert "测试用例1" in content
assert "测试用例2" in content
assert "50.0%" in content or "50.0" in content
def test_generate_html_report_create_directory(report_manager, test_suite_result, tmp_path):
"""测试生成HTML报告(创建目录)"""
report_path = tmp_path / "reports" / "nested" / "test_report.html"
result_path = report_manager.generate_html_report(
test_suite_result,
report_path
)
assert result_path == str(report_path)
assert report_path.exists()
assert report_path.parent.exists()
def test_generate_json_report_success(report_manager, test_suite_result, tmp_path):
"""测试生成JSON报告(成功)"""
report_path = tmp_path / "test_report.json"
result_path = report_manager.generate_json_report(
test_suite_result,
report_path
)
assert result_path == str(report_path)
assert report_path.exists()
import json
content = report_path.read_text(encoding="utf-8")
data = json.loads(content)
assert data["suite_name"] == "Test Suite"
assert data["total"] == 2
assert data["passed"] == 1
assert data["failed"] == 1
assert data["skipped"] == 0
assert len(data["results"]) == 2
def test_generate_json_report_create_directory(report_manager, test_suite_result, tmp_path):
"""测试生成JSON报告(创建目录)"""
report_path = tmp_path / "reports" / "nested" / "test_report.json"
result_path = report_manager.generate_json_report(
test_suite_result,
report_path
)
assert result_path == str(report_path)
assert report_path.exists()
assert report_path.parent.exists()
def test_generate_html_report_logger_call(report_manager, test_suite_result, tmp_path):
"""测试生成HTML报告时调用日志记录器"""
report_path = tmp_path / "test_report.html"
report_manager.generate_html_report(
test_suite_result,
report_path
)
report_manager.logger.info.assert_called()
def test_generate_json_report_logger_call(report_manager, test_suite_result, tmp_path):
"""测试生成JSON报告时调用日志记录器"""
report_path = tmp_path / "test_report.json"
report_manager.generate_json_report(
test_suite_result,
report_path
)
report_manager.logger.info.assert_called()
def test_generate_html_report_content_structure(report_manager, test_suite_result, tmp_path):
"""测试HTML报告内容结构"""
report_path = tmp_path / "test_report.html"
report_manager.generate_html_report(
test_suite_result,
report_path,
title="API测试报告"
)
content = report_path.read_text(encoding="utf-8")
assert "<!DOCTYPE html>" in content
assert "<html" in content
assert "<head>" in content
assert "<body>" in content
assert "API测试报告" in content
assert "总用例数" in content
assert "通过" in content
assert "失败" in content
assert "跳过" in content
assert "通过率" in content
assert "执行时长" in content
assert "测试结果详情" in content
assert "</html>" in content
def test_generate_html_report_with_all_passed(report_manager, tmp_path):
"""测试生成HTML报告(全部通过)"""
test_cases = [
TestCase(
id="TC001",
name="测试用例1",
description="测试用例1",
module="test",
endpoint="/api/test1",
method=HTTPMethod.GET,
headers={},
enabled=True
),
TestCase(
id="TC002",
name="测试用例2",
description="测试用例2",
module="test",
endpoint="/api/test2",
method=HTTPMethod.POST,
headers={},
enabled=True
)
]
results = [
TestResult(
test_case=test_cases[0],
passed=True,
status_code=200,
response_body={"message": "success"},
response_headers={"Content-Type": "application/json"},
performance=PerformanceMetrics(
timestamp=datetime.now(),
response_time=123,
request_size=0,
response_size=0
)
),
TestResult(
test_case=test_cases[1],
passed=True,
status_code=200,
response_body={"message": "success"},
response_headers={"Content-Type": "application/json"},
performance=PerformanceMetrics(
timestamp=datetime.now(),
response_time=456,
request_size=0,
response_size=0
)
)
]
test_suite_result = TestSuiteResult(
suite_name="Test Suite",
total=2,
passed=2,
failed=0,
skipped=0,
results=results,
start_time=datetime.now()
)
report_path = tmp_path / "test_report.html"
report_manager.generate_html_report(test_suite_result, report_path)
content = report_path.read_text(encoding="utf-8")
assert "100.0%" in content or "100.0" in content
def test_generate_html_report_with_all_failed(report_manager, tmp_path):
"""测试生成HTML报告(全部失败)"""
test_cases = [
TestCase(
id="TC001",
name="测试用例1",
description="测试用例1",
module="test",
endpoint="/api/test1",
method=HTTPMethod.GET,
headers={},
enabled=True
),
TestCase(
id="TC002",
name="测试用例2",
description="测试用例2",
module="test",
endpoint="/api/test2",
method=HTTPMethod.POST,
headers={},
enabled=True
)
]
results = [
TestResult(
test_case=test_cases[0],
passed=False,
status_code=500,
response_body={"error": "error"},
response_headers={"Content-Type": "application/json"},
performance=PerformanceMetrics(
timestamp=datetime.now(),
response_time=123,
request_size=0,
response_size=0
),
error_message="错误1"
),
TestResult(
test_case=test_cases[1],
passed=False,
status_code=404,
response_body={"error": "not found"},
response_headers={"Content-Type": "application/json"},
performance=PerformanceMetrics(
timestamp=datetime.now(),
response_time=456,
request_size=0,
response_size=0
),
error_message="错误2"
)
]
test_suite_result = TestSuiteResult(
suite_name="Test Suite",
total=2,
passed=0,
failed=2,
skipped=0,
results=results,
start_time=datetime.now()
)
report_path = tmp_path / "test_report.html"
report_manager.generate_html_report(test_suite_result, report_path)
content = report_path.read_text(encoding="utf-8")
assert "0.0%" in content or "0.0" in content
assert "错误1" in content
assert "错误2" in content
@@ -0,0 +1,297 @@
"""测试数据管理器单元测试"""
import pytest
from pathlib import Path
from unittest.mock import Mock
from apitest.data.test_data_manager import TestDataManager
from apitest.models.test_models import TestCase, HTTPMethod
from apitest.models.exceptions import TestRunException
@pytest.fixture
def mock_logger():
"""创建模拟日志记录器"""
return Mock()
@pytest.fixture
def data_manager(mock_logger):
"""创建测试数据管理器实例"""
return TestDataManager(logger=mock_logger)
@pytest.fixture
def sample_test_cases():
"""创建示例测试用例"""
return [
TestCase(
id="TC001",
name="测试用例1",
description="测试用例1",
module="test",
endpoint="/api/test1",
method=HTTPMethod.GET,
headers={},
enabled=True
),
TestCase(
id="TC002",
name="测试用例2",
description="测试用例2",
module="test",
endpoint="/api/test2",
method=HTTPMethod.POST,
headers={},
enabled=True
)
]
def test_load_test_cases_from_json_success(data_manager, tmp_path):
"""测试从JSON文件加载测试用例(成功)"""
import json
test_data = [
{
"id": "TC001",
"name": "测试用例1",
"description": "测试用例1",
"module": "test",
"endpoint": "/api/test1",
"method": "GET",
"headers": {},
"enabled": True
},
{
"id": "TC002",
"name": "测试用例2",
"description": "测试用例2",
"module": "test",
"endpoint": "/api/test2",
"method": "POST",
"headers": {},
"enabled": True
}
]
test_file = tmp_path / "test_cases.json"
with open(test_file, "w", encoding="utf-8") as f:
json.dump(test_data, f)
test_cases = data_manager.load_test_cases_from_json(test_file)
assert len(test_cases) == 2
assert test_cases[0].id == "TC001"
assert test_cases[1].id == "TC002"
assert test_cases[0].method == HTTPMethod.GET
assert test_cases[1].method == HTTPMethod.POST
def test_load_test_cases_from_json_file_not_found(data_manager, tmp_path):
"""测试从JSON文件加载测试用例(文件不存在)"""
test_file = tmp_path / "nonexistent.json"
with pytest.raises(TestRunException) as exc_info:
data_manager.load_test_cases_from_json(test_file)
assert "测试用例文件不存在" in str(exc_info.value)
def test_load_test_cases_from_json_invalid_json(data_manager, tmp_path):
"""测试从JSON文件加载测试用例(无效JSON)"""
test_file = tmp_path / "invalid.json"
with open(test_file, "w", encoding="utf-8") as f:
f.write("{ invalid json }")
with pytest.raises(TestRunException) as exc_info:
data_manager.load_test_cases_from_json(test_file)
assert "JSON文件解析失败" in str(exc_info.value)
def test_load_test_cases_from_json_invalid_method(data_manager, tmp_path):
"""测试从JSON文件加载测试用例(无效HTTP方法)"""
import json
test_data = [
{
"id": "TC001",
"name": "测试用例1",
"description": "测试用例1",
"module": "test",
"endpoint": "/api/test1",
"method": "INVALID",
"headers": {},
"enabled": True
}
]
test_file = tmp_path / "test_cases.json"
with open(test_file, "w", encoding="utf-8") as f:
json.dump(test_data, f)
test_cases = data_manager.load_test_cases_from_json(test_file)
assert len(test_cases) == 1
assert test_cases[0].method == HTTPMethod.GET
def test_load_test_data_from_csv_success(data_manager, tmp_path):
"""测试从CSV文件加载测试数据(成功)"""
test_file = tmp_path / "test_data.csv"
with open(test_file, "w", encoding="utf-8", newline="") as f:
f.write("param1,param2,param3\n")
f.write("value1,value2,value3\n")
f.write("value4,value5,value6\n")
test_data = data_manager.load_test_data_from_csv(test_file)
assert len(test_data) == 2
assert test_data[0] == {"param1": "value1", "param2": "value2", "param3": "value3"}
assert test_data[1] == {"param1": "value4", "param2": "value5", "param3": "value6"}
def test_load_test_data_from_csv_file_not_found(data_manager, tmp_path):
"""测试从CSV文件加载测试数据(文件不存在)"""
test_file = tmp_path / "nonexistent.csv"
with pytest.raises(TestRunException) as exc_info:
data_manager.load_test_data_from_csv(test_file)
assert "测试数据文件不存在" in str(exc_info.value)
def test_parameterize_test_case_success(data_manager, sample_test_cases):
"""测试参数化测试用例(成功)"""
test_data = [
{"params": {"id": "1"}, "body": {"name": "test1"}},
{"params": {"id": "2"}, "body": {"name": "test2"}}
]
parameterized_cases = data_manager.parameterize_test_case(
sample_test_cases[0],
test_data
)
assert len(parameterized_cases) == 2
assert parameterized_cases[0].id == "TC001_1"
assert parameterized_cases[0].name == "测试用例1 (数据集 1)"
assert parameterized_cases[0].params == {"id": "1"}
assert parameterized_cases[0].body == {"name": "test1"}
assert parameterized_cases[1].id == "TC001_2"
assert parameterized_cases[1].name == "测试用例1 (数据集 2)"
assert parameterized_cases[1].params == {"id": "2"}
assert parameterized_cases[1].body == {"name": "test2"}
def test_parameterize_test_case_empty_data(data_manager, sample_test_cases):
"""测试参数化测试用例(空数据)"""
test_data = []
parameterized_cases = data_manager.parameterize_test_case(
sample_test_cases[0],
test_data
)
assert len(parameterized_cases) == 0
def test_save_test_cases_to_json_success(data_manager, sample_test_cases, tmp_path):
"""测试保存测试用例到JSON文件(成功)"""
output_file = tmp_path / "output.json"
data_manager.save_test_cases_to_json(sample_test_cases, output_file)
assert output_file.exists()
import json
with open(output_file, "r", encoding="utf-8") as f:
data = json.load(f)
assert len(data) == 2
assert data[0]["id"] == "TC001"
assert data[1]["id"] == "TC002"
def test_save_test_cases_to_json_create_directory(data_manager, sample_test_cases, tmp_path):
"""测试保存测试用例到JSON文件(创建目录)"""
output_file = tmp_path / "nested" / "dir" / "output.json"
data_manager.save_test_cases_to_json(sample_test_cases, output_file)
assert output_file.exists()
assert output_file.parent.exists()
def test_save_test_data_to_csv_success(data_manager, tmp_path):
"""测试保存测试数据到CSV文件(成功)"""
test_data = [
{"param1": "value1", "param2": "value2"},
{"param1": "value3", "param2": "value4"}
]
output_file = tmp_path / "output.csv"
data_manager.save_test_data_to_csv(test_data, output_file)
assert output_file.exists()
import csv
with open(output_file, "r", encoding="utf-8") as f:
reader = csv.DictReader(f)
rows = list(reader)
assert len(rows) == 2
assert rows[0]["param1"] == "value1"
assert rows[1]["param1"] == "value3"
def test_save_test_data_to_csv_with_fieldnames(data_manager, tmp_path):
"""测试保存测试数据到CSV文件(指定字段名)"""
test_data = [
{"param1": "value1", "param2": "value2", "param3": "value3"},
{"param1": "value4", "param2": "value5", "param3": "value6"}
]
output_file = tmp_path / "output.csv"
data_manager.save_test_data_to_csv(
test_data,
output_file,
fieldnames=["param1", "param2"]
)
assert output_file.exists()
import csv
with open(output_file, "r", encoding="utf-8") as f:
reader = csv.DictReader(f)
rows = list(reader)
fieldnames = reader.fieldnames
assert len(rows) == 2
assert fieldnames == ["param1", "param2"]
def test_save_test_data_to_csv_empty_data(data_manager, tmp_path):
"""测试保存测试数据到CSV文件(空数据)"""
test_data = []
output_file = tmp_path / "output.csv"
with pytest.raises(TestRunException) as exc_info:
data_manager.save_test_data_to_csv(test_data, output_file)
assert "测试数据为空" in str(exc_info.value)
def test_save_test_data_to_csv_create_directory(data_manager, tmp_path):
"""测试保存测试数据到CSV文件(创建目录)"""
test_data = [{"param1": "value1"}]
output_file = tmp_path / "nested" / "dir" / "output.csv"
data_manager.save_test_data_to_csv(test_data, output_file)
assert output_file.exists()
assert output_file.parent.exists()
@@ -0,0 +1,505 @@
import pytest
from unittest.mock import MagicMock, patch
from datetime import datetime
from apitest.models.test_models import (
TestCase, TestResult, TestSuiteResult, HTTPMethod, PerformanceMetrics
)
from apitest.core.test_engine import TestEngine
from apitest.core.validation_engine import ValidationEngine
from apitest.models.exceptions import TestRunException
class TestTestEngine:
"""测试TestEngine测试引擎"""
@pytest.fixture
def mock_api_client(self):
"""模拟API客户端"""
api_client = MagicMock()
api_client.request.return_value = {
"status_code": 200,
"response_body": {"message": "success"},
"response_headers": {"Content-Type": "application/json"},
"performance": PerformanceMetrics(
timestamp=datetime.now(),
response_time=1000,
request_size=100,
response_size=200
)
}
return api_client
@pytest.fixture
def mock_auth_manager(self):
"""模拟认证管理器"""
auth_manager = MagicMock()
auth_manager.get_auth_headers.return_value = {"Authorization": "Bearer token"}
return auth_manager
@pytest.fixture
def mock_validation_engine(self):
"""模拟验证引擎"""
validation_engine = MagicMock()
validation_engine.validate_response.return_value = (True, "")
return validation_engine
@pytest.fixture
def test_engine(self, mock_api_client, mock_auth_manager, mock_validation_engine):
"""创建测试引擎实例"""
return TestEngine(
api_client=mock_api_client,
auth_manager=mock_auth_manager,
validation_engine=mock_validation_engine
)
def test_test_engine_initialization(self, test_engine):
"""测试测试引擎初始化"""
assert test_engine.api_client is not None
assert test_engine.auth_manager is not None
assert test_engine.validation_engine is not None
assert test_engine._context == {}
def test_set_context(self, test_engine):
"""测试设置上下文变量"""
test_engine.set_context("user_id", "12345")
assert test_engine.get_context("user_id") == "12345"
def test_get_context_with_default(self, test_engine):
"""测试获取上下文变量(带默认值)"""
assert test_engine.get_context("nonexistent", "default") == "default"
def test_topological_sort_no_dependencies(self, test_engine):
"""测试拓扑排序(无依赖)"""
test_cases = [
TestCase(
id="TC001",
name="测试1",
description="测试用例1",
module="test",
endpoint="/api/test1",
method=HTTPMethod.GET,
headers={}
),
TestCase(
id="TC002",
name="测试2",
description="测试用例2",
module="test",
endpoint="/api/test2",
method=HTTPMethod.GET,
headers={}
)
]
sorted_cases = test_engine._topological_sort(test_cases)
assert len(sorted_cases) == 2
def test_topological_sort_with_dependencies(self, test_engine):
"""测试拓扑排序(有依赖)"""
test_cases = [
TestCase(
id="TC001",
name="测试1",
description="测试用例1",
module="test",
endpoint="/api/test1",
method=HTTPMethod.GET,
headers={},
dependencies=[]
),
TestCase(
id="TC002",
name="测试2",
description="测试用例2",
module="test",
endpoint="/api/test2",
method=HTTPMethod.GET,
headers={},
dependencies=["TC001"]
),
TestCase(
id="TC003",
name="测试3",
description="测试用例3",
module="test",
endpoint="/api/test3",
method=HTTPMethod.GET,
headers={},
dependencies=["TC002"]
)
]
sorted_cases = test_engine._topological_sort(test_cases)
assert len(sorted_cases) == 3
assert sorted_cases[0].id == "TC001"
assert sorted_cases[1].id == "TC002"
assert sorted_cases[2].id == "TC003"
def test_topological_sort_circular_dependency(self, test_engine):
"""测试拓扑排序(循环依赖)"""
test_cases = [
TestCase(
id="TC001",
name="测试1",
description="测试用例1",
module="test",
endpoint="/api/test1",
method=HTTPMethod.GET,
headers={},
dependencies=["TC002"]
),
TestCase(
id="TC002",
name="测试2",
description="测试用例2",
module="test",
endpoint="/api/test2",
method=HTTPMethod.GET,
headers={},
dependencies=["TC001"]
)
]
with pytest.raises(TestRunException, match="存在循环依赖"):
test_engine._topological_sort(test_cases)
def test_resolve_context_variables_string(self, test_engine):
"""测试解析上下文变量(字符串)"""
test_engine.set_context("user_id", "12345")
result = test_engine._resolve_context_variables("${user_id}")
assert result == "12345"
def test_resolve_context_variables_dict(self, test_engine):
"""测试解析上下文变量(字典)"""
test_engine.set_context("user_id", "12345")
data = {"user_id": "${user_id}", "name": "test"}
result = test_engine._resolve_context_variables(data)
assert result == {"user_id": "12345", "name": "test"}
def test_resolve_context_variables_list(self, test_engine):
"""测试解析上下文变量(列表)"""
test_engine.set_context("user_id", "12345")
data = ["${user_id}", "test"]
result = test_engine._resolve_context_variables(data)
assert result == ["12345", "test"]
def test_execute_test_case_success(self, test_engine):
"""测试执行测试用例(成功)"""
test_case = TestCase(
id="TC001",
name="测试用例",
description="测试用例描述",
module="test",
endpoint="/api/test",
method=HTTPMethod.GET,
headers={},
validations=[{"type": "status_code", "value": 200}]
)
result = test_engine._execute_test_case(test_case)
assert isinstance(result, TestResult)
assert result.passed == True
assert result.status_code == 200
assert result.test_case == test_case
def test_execute_test_case_with_auth(self, test_engine):
"""测试执行测试用例(带认证)"""
test_case = TestCase(
id="TC001",
name="测试用例",
description="测试用例描述",
module="test",
endpoint="/api/test",
method=HTTPMethod.GET,
headers={},
auth_required=True
)
result = test_engine._execute_test_case(test_case)
assert result.passed == True
test_engine.auth_manager.get_auth_headers.assert_called_once()
def test_execute_test_case_failure(self, test_engine, mock_validation_engine):
"""测试执行测试用例(失败)"""
mock_validation_engine.validate_response.return_value = (False, "验证失败")
test_case = TestCase(
id="TC001",
name="测试用例",
description="测试用例描述",
module="test",
endpoint="/api/test",
method=HTTPMethod.GET,
headers={},
validations=[{"type": "status_code", "value": 200}]
)
result = test_engine._execute_test_case(test_case)
assert result.passed == False
assert result.error_message == "验证失败"
def test_execute_test_case_with_setup(self, test_engine):
"""测试执行测试用例(带前置操作)"""
test_case = TestCase(
id="TC001",
name="测试用例",
description="测试用例描述",
module="test",
endpoint="/api/test",
method=HTTPMethod.GET,
headers={},
setup={"type": "set_context", "key": "test_key", "value": "test_value"}
)
result = test_engine._execute_test_case(test_case)
assert test_engine.get_context("test_key") == "test_value"
def test_execute_test_case_with_teardown(self, test_engine):
"""测试执行测试用例(带后置操作)"""
test_engine.set_context("test_key", "test_value")
test_case = TestCase(
id="TC001",
name="测试用例",
description="测试用例描述",
module="test",
endpoint="/api/test",
method=HTTPMethod.GET,
headers={},
teardown={"type": "clear_context", "key": "test_key"}
)
result = test_engine._execute_test_case(test_case)
assert test_engine.get_context("test_key") is None
def test_execute_test_suite(self, test_engine):
"""测试执行测试套件"""
test_cases = [
TestCase(
id="TC001",
name="测试1",
description="测试用例1",
module="test",
endpoint="/api/test1",
method=HTTPMethod.GET,
headers={}
),
TestCase(
id="TC002",
name="测试2",
description="测试用例2",
module="test",
endpoint="/api/test2",
method=HTTPMethod.GET,
headers={}
)
]
result = test_engine.execute_test_suite(test_cases)
assert isinstance(result, TestSuiteResult)
assert len(result.results) == 2
assert result.passed == 2
assert result.failed == 0
def test_execute_test_suite_with_dependencies(self, test_engine):
"""测试执行测试套件(有依赖)"""
test_cases = [
TestCase(
id="TC001",
name="测试1",
description="测试用例1",
module="test",
endpoint="/api/test1",
method=HTTPMethod.GET,
headers={},
dependencies=[]
),
TestCase(
id="TC002",
name="测试2",
description="测试用例2",
module="test",
endpoint="/api/test2",
method=HTTPMethod.GET,
headers={},
dependencies=["TC001"]
)
]
result = test_engine.execute_test_suite(test_cases)
assert len(result.results) == 2
assert result.passed == 2
def test_execute_test_suite_stop_on_failure(self, test_engine, mock_validation_engine):
"""测试执行测试套件(失败时停止)"""
mock_validation_engine.validate_response.return_value = (False, "验证失败")
test_cases = [
TestCase(
id="TC001",
name="测试1",
description="测试用例1",
module="test",
endpoint="/api/test1",
method=HTTPMethod.GET,
headers={}
),
TestCase(
id="TC002",
name="测试2",
description="测试用例2",
module="test",
endpoint="/api/test2",
method=HTTPMethod.GET,
headers={}
)
]
result = test_engine.execute_test_suite(test_cases, stop_on_failure=True)
assert len(result.results) == 1
assert result.passed == 0
assert result.failed == 1
def test_execute_test_suite_skip_disabled(self, test_engine):
"""测试执行测试套件(跳过已禁用的用例)"""
test_cases = [
TestCase(
id="TC001",
name="测试1",
description="测试用例1",
module="test",
endpoint="/api/test1",
method=HTTPMethod.GET,
headers={},
enabled=False
),
TestCase(
id="TC002",
name="测试2",
description="测试用例2",
module="test",
endpoint="/api/test2",
method=HTTPMethod.GET,
headers={},
enabled=True
)
]
result = test_engine.execute_test_suite(test_cases)
assert len(result.results) == 1
assert result.skipped == 1
def test_execute_test_cases_by_filter_module(self, test_engine):
"""测试按模块过滤执行测试用例"""
test_cases = [
TestCase(
id="TC001",
name="测试1",
description="测试用例1",
module="module1",
endpoint="/api/test1",
method=HTTPMethod.GET,
headers={}
),
TestCase(
id="TC002",
name="测试2",
description="测试用例2",
module="module2",
endpoint="/api/test2",
method=HTTPMethod.GET,
headers={}
)
]
result = test_engine.execute_test_cases_by_filter(test_cases, module_filter="module1")
assert len(result.results) == 1
assert result.results[0].test_case.module == "module1"
def test_execute_test_cases_by_filter_tag(self, test_engine):
"""测试按标签过滤执行测试用例"""
test_cases = [
TestCase(
id="TC001",
name="测试1",
description="测试用例1",
module="test",
endpoint="/api/test1",
method=HTTPMethod.GET,
headers={},
tags=["smoke", "regression"]
),
TestCase(
id="TC002",
name="测试2",
description="测试用例2",
module="test",
endpoint="/api/test2",
method=HTTPMethod.GET,
headers={},
tags=["regression"]
)
]
result = test_engine.execute_test_cases_by_filter(test_cases, tag_filter=["smoke"])
assert len(result.results) == 1
assert "smoke" in result.results[0].test_case.tags
def test_execute_test_cases_by_filter_priority(self, test_engine):
"""测试按优先级过滤执行测试用例"""
test_cases = [
TestCase(
id="TC001",
name="测试1",
description="测试用例1",
module="test",
endpoint="/api/test1",
method=HTTPMethod.GET,
headers={},
priority=1
),
TestCase(
id="TC002",
name="测试2",
description="测试用例2",
module="test",
endpoint="/api/test2",
method=HTTPMethod.GET,
headers={},
priority=2
)
]
result = test_engine.execute_test_cases_by_filter(test_cases, priority_filter=1)
assert len(result.results) == 1
assert result.results[0].test_case.priority == 1
def test_extract_response_data(self, test_engine):
"""测试提取响应数据到上下文"""
test_case = TestCase(
id="TC001",
name="测试用例",
description="测试用例描述",
module="test",
endpoint="/api/test",
method=HTTPMethod.GET,
headers={},
validations=[{"type": "extract", "field": "user_id", "var_name": "extracted_id"}]
)
response_body = {"user_id": "12345", "name": "test"}
test_engine._extract_response_data(test_case, response_body)
assert test_engine.get_context("extracted_id") == "12345"
@@ -0,0 +1,373 @@
"""测试编排器单元测试"""
import pytest
from pathlib import Path
from unittest.mock import Mock, MagicMock, patch
from apitest.orchestrator.test_orchestrator import TestOrchestrator
from apitest.models.test_models import TestCase, TestSuiteResult, HTTPMethod
from apitest.models.exceptions import TestRunException
@pytest.fixture
def mock_config_manager():
"""创建模拟配置管理器"""
config_manager = Mock()
config_manager.get_base_url.return_value = "http://localhost:8080"
config_manager.get_timeout.return_value = 30
config_manager.get_log_level.return_value = "INFO"
config_manager.get_log_format.return_value = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
return config_manager
@pytest.fixture
def mock_logger_manager():
"""创建模拟日志管理器"""
logger_manager = Mock()
logger = Mock()
logger_manager.get_logger.return_value = logger
return logger_manager
@pytest.fixture
def test_orchestrator(mock_config_manager, mock_logger_manager):
"""创建测试编排器实例"""
return TestOrchestrator(
config_manager=mock_config_manager,
logger_manager=mock_logger_manager
)
@pytest.fixture
def sample_test_cases():
"""创建示例测试用例"""
return [
TestCase(
id="TC001",
name="测试用例1",
description="测试用例1",
module="test",
endpoint="/api/test1",
method=HTTPMethod.GET,
headers={},
enabled=True
),
TestCase(
id="TC002",
name="测试用例2",
description="测试用例2",
module="test",
endpoint="/api/test2",
method=HTTPMethod.POST,
headers={},
enabled=True
)
]
def test_init_test_orchestrator(mock_config_manager, mock_logger_manager):
"""测试初始化测试编排器"""
orchestrator = TestOrchestrator(
config_manager=mock_config_manager,
logger_manager=mock_logger_manager
)
assert orchestrator.config_manager == mock_config_manager
assert orchestrator.logger_manager == mock_logger_manager
assert orchestrator.api_client is not None
assert orchestrator.auth_manager is not None
assert orchestrator.validation_engine is not None
assert orchestrator.test_engine is not None
assert orchestrator.report_manager is not None
assert orchestrator.logger is not None
def test_init_test_orchestrator_without_managers():
"""测试初始化测试编排器(不提供管理器)"""
mock_logger = Mock()
orchestrator = TestOrchestrator(logger=mock_logger)
assert orchestrator.config_manager is not None
assert orchestrator.logger_manager is not None
assert orchestrator.api_client is not None
assert orchestrator.auth_manager is not None
assert orchestrator.validation_engine is not None
assert orchestrator.test_engine is not None
assert orchestrator.report_manager is not None
assert orchestrator.logger is not None
def test_load_test_cases_success(test_orchestrator, tmp_path):
"""测试加载测试用例(成功)"""
import json
test_data = [
{
"id": "TC001",
"name": "测试用例1",
"description": "测试用例1",
"module": "test",
"endpoint": "/api/test1",
"method": "GET",
"headers": {},
"enabled": True
},
{
"id": "TC002",
"name": "测试用例2",
"description": "测试用例2",
"module": "test",
"endpoint": "/api/test2",
"method": "POST",
"headers": {},
"enabled": True
}
]
test_file = tmp_path / "test_cases.json"
with open(test_file, "w", encoding="utf-8") as f:
json.dump(test_data, f)
test_cases = test_orchestrator.load_test_cases(test_file)
assert len(test_cases) == 2
assert test_cases[0].id == "TC001"
assert test_cases[1].id == "TC002"
def test_load_test_cases_file_not_found(test_orchestrator, tmp_path):
"""测试加载测试用例(文件不存在)"""
test_file = tmp_path / "nonexistent.json"
with pytest.raises(TestRunException) as exc_info:
test_orchestrator.load_test_cases(test_file)
assert "测试用例文件不存在" in str(exc_info.value)
def test_load_test_cases_invalid_json(test_orchestrator, tmp_path):
"""测试加载测试用例(无效JSON"""
test_file = tmp_path / "invalid.json"
with open(test_file, "w", encoding="utf-8") as f:
f.write("invalid json")
with pytest.raises(TestRunException) as exc_info:
test_orchestrator.load_test_cases(test_file)
assert "加载测试用例失败" in str(exc_info.value)
def test_load_test_cases_with_all_fields(test_orchestrator, tmp_path):
"""测试加载测试用例(包含所有字段)"""
import json
test_data = [
{
"id": "TC001",
"name": "测试用例1",
"description": "测试用例1",
"module": "test",
"endpoint": "/api/test1",
"method": "GET",
"headers": {"Content-Type": "application/json"},
"params": {"param1": "value1"},
"body": {"key": "value"},
"dependencies": ["TC002"],
"tags": ["tag1", "tag2"],
"priority": 1,
"enabled": True,
"timeout": 10,
"validations": [{"type": "status_code", "value": 200}],
"extract_config": [{"type": "extract", "field": "id", "var_name": "user_id"}]
}
]
test_file = tmp_path / "test_cases.json"
with open(test_file, "w", encoding="utf-8") as f:
json.dump(test_data, f)
test_cases = test_orchestrator.load_test_cases(test_file)
assert len(test_cases) == 1
assert test_cases[0].id == "TC001"
assert test_cases[0].method == HTTPMethod.GET
assert test_cases[0].headers == {"Content-Type": "application/json"}
assert test_cases[0].params == {"param1": "value1"}
assert test_cases[0].body == {"key": "value"}
assert test_cases[0].dependencies == ["TC002"]
assert test_cases[0].tags == ["tag1", "tag2"]
assert test_cases[0].priority == 1
assert test_cases[0].enabled == True
assert test_cases[0].timeout == 10
assert len(test_cases[0].validations) == 1
def test_run_test_suite_success(test_orchestrator, sample_test_cases, tmp_path):
"""测试运行测试套件(成功)"""
report_path = tmp_path / "test_report.html"
with patch.object(test_orchestrator.test_engine, 'execute_test_suite') as mock_execute:
mock_result = Mock(spec=TestSuiteResult)
mock_result.suite_name = "Test Suite"
mock_result.total = 2
mock_result.passed = 2
mock_result.failed = 0
mock_result.skipped = 0
mock_result.pass_rate = 100.0
mock_result.duration = 1.0
mock_execute.return_value = mock_result
result = test_orchestrator.run_test_suite(
sample_test_cases,
generate_report=False
)
assert result == mock_result
mock_execute.assert_called_once_with(sample_test_cases, stop_on_failure=False)
def test_run_test_suite_with_report(test_orchestrator, sample_test_cases, tmp_path):
"""测试运行测试套件(生成报告)"""
report_path = tmp_path / "test_report.html"
with patch.object(test_orchestrator.test_engine, 'execute_test_suite') as mock_execute, \
patch.object(test_orchestrator.report_manager, 'generate_html_report') as mock_report:
mock_result = Mock(spec=TestSuiteResult)
mock_result.suite_name = "Test Suite"
mock_result.total = 2
mock_result.passed = 2
mock_result.failed = 0
mock_result.skipped = 0
mock_result.pass_rate = 100.0
mock_result.duration = 1.0
mock_execute.return_value = mock_result
result = test_orchestrator.run_test_suite(
sample_test_cases,
generate_report=True,
report_format="html",
report_path=report_path
)
assert result == mock_result
mock_report.assert_called_once()
def test_run_test_suite_stop_on_failure(test_orchestrator, sample_test_cases):
"""测试运行测试套件(失败时停止)"""
with patch.object(test_orchestrator.test_engine, 'execute_test_suite') as mock_execute:
mock_result = Mock(spec=TestSuiteResult)
mock_result.suite_name = "Test Suite"
mock_result.total = 2
mock_result.passed = 1
mock_result.failed = 1
mock_result.skipped = 0
mock_result.pass_rate = 50.0
mock_result.duration = 0.5
mock_execute.return_value = mock_result
result = test_orchestrator.run_test_suite(
sample_test_cases,
stop_on_failure=True,
generate_report=False
)
assert result == mock_result
mock_execute.assert_called_once_with(sample_test_cases, stop_on_failure=True)
def test_run_test_suite_by_filter_module(test_orchestrator, sample_test_cases):
"""测试按模块过滤运行测试套件"""
with patch.object(test_orchestrator.test_engine, 'execute_test_cases_by_filter') as mock_execute:
mock_result = Mock(spec=TestSuiteResult)
mock_result.suite_name = "Test Suite"
mock_result.total = 2
mock_result.passed = 2
mock_result.failed = 0
mock_result.skipped = 0
mock_result.pass_rate = 100.0
mock_result.duration = 1.0
mock_execute.return_value = mock_result
result = test_orchestrator.run_test_suite_by_filter(
sample_test_cases,
module_filter="test",
generate_report=False
)
assert result == mock_result
mock_execute.assert_called_once_with(
sample_test_cases,
module_filter="test",
tag_filter=None,
priority_filter=None
)
def test_run_test_suite_by_filter_tag(test_orchestrator, sample_test_cases):
"""测试按标签过滤运行测试套件"""
with patch.object(test_orchestrator.test_engine, 'execute_test_cases_by_filter') as mock_execute:
mock_result = Mock(spec=TestSuiteResult)
mock_result.suite_name = "Test Suite"
mock_result.total = 2
mock_result.passed = 2
mock_result.failed = 0
mock_result.skipped = 0
mock_result.pass_rate = 100.0
mock_result.duration = 1.0
mock_execute.return_value = mock_result
result = test_orchestrator.run_test_suite_by_filter(
sample_test_cases,
tag_filter=["smoke"],
generate_report=False
)
assert result == mock_result
mock_execute.assert_called_once_with(
sample_test_cases,
module_filter=None,
tag_filter=["smoke"],
priority_filter=None
)
def test_run_test_suite_by_filter_priority(test_orchestrator, sample_test_cases):
"""测试按优先级过滤运行测试套件"""
with patch.object(test_orchestrator.test_engine, 'execute_test_cases_by_filter') as mock_execute:
mock_result = Mock(spec=TestSuiteResult)
mock_result.suite_name = "Test Suite"
mock_result.total = 2
mock_result.passed = 2
mock_result.failed = 0
mock_result.skipped = 0
mock_result.pass_rate = 100.0
mock_result.duration = 1.0
mock_execute.return_value = mock_result
result = test_orchestrator.run_test_suite_by_filter(
sample_test_cases,
priority_filter=1,
generate_report=False
)
assert result == mock_result
mock_execute.assert_called_once_with(
sample_test_cases,
module_filter=None,
tag_filter=None,
priority_filter=1
)
def test_set_base_url(test_orchestrator):
"""测试设置基础URL"""
test_orchestrator.set_base_url("http://new-api.example.com")
assert test_orchestrator.api_client.base_url == "http://new-api.example.com"
def test_set_auth_token(test_orchestrator):
"""测试设置认证令牌"""
test_orchestrator.set_auth_token("test-token-123")
assert test_orchestrator.auth_manager.get_token() == "test-token-123"
@@ -0,0 +1,480 @@
import pytest
from apitest.models.test_models import TestCase, HTTPMethod, PerformanceMetrics
from apitest.core.validation_engine import ValidationEngine
from apitest.models.exceptions import ValidationException
class TestValidationEngine:
"""测试ValidationEngine验证引擎"""
@pytest.fixture
def validation_engine(self):
"""创建验证引擎实例"""
return ValidationEngine()
def test_validate_status_code_success(self, validation_engine):
"""测试状态码验证成功"""
test_case = TestCase(
id="TC001",
name="测试状态码",
description="测试状态码验证",
module="test",
endpoint="/api/test",
method=HTTPMethod.GET,
headers={},
validations=[{"type": "status_code", "value": 200}]
)
passed, error = validation_engine.validate_response(
test_case,
200,
{"message": "success"},
{}
)
assert passed == True
assert error == ""
def test_validate_status_code_failure(self, validation_engine):
"""测试状态码验证失败"""
test_case = TestCase(
id="TC001",
name="测试状态码",
description="测试状态码验证",
module="test",
endpoint="/api/test",
method=HTTPMethod.GET,
headers={},
validations=[{"type": "status_code", "value": 200}]
)
passed, error = validation_engine.validate_response(
test_case,
404,
{"message": "not found"},
{}
)
assert passed == False
assert "状态码验证失败" in error
def test_validate_contains_success(self, validation_engine):
"""测试包含验证成功"""
test_case = TestCase(
id="TC002",
name="测试包含",
description="测试包含验证",
module="test",
endpoint="/api/test",
method=HTTPMethod.GET,
headers={},
validations=[{"type": "contains", "value": "success"}]
)
passed, error = validation_engine.validate_response(
test_case,
200,
{"message": "operation success"},
{}
)
assert passed == True
assert error == ""
def test_validate_contains_failure(self, validation_engine):
"""测试包含验证失败"""
test_case = TestCase(
id="TC002",
name="测试包含",
description="测试包含验证",
module="test",
endpoint="/api/test",
method=HTTPMethod.GET,
headers={},
validations=[{"type": "contains", "value": "error"}]
)
passed, error = validation_engine.validate_response(
test_case,
200,
{"message": "operation success"},
{}
)
assert passed == False
assert "包含验证失败" in error
def test_validate_contains_with_field(self, validation_engine):
"""测试字段包含验证"""
test_case = TestCase(
id="TC003",
name="测试字段包含",
description="测试字段包含验证",
module="test",
endpoint="/api/test",
method=HTTPMethod.GET,
headers={},
validations=[{"type": "contains", "field": "message", "value": "success"}]
)
passed, error = validation_engine.validate_response(
test_case,
200,
{"message": "operation success"},
{}
)
assert passed == True
def test_validate_equals_success(self, validation_engine):
"""测试相等验证成功"""
test_case = TestCase(
id="TC004",
name="测试相等",
description="测试相等验证",
module="test",
endpoint="/api/test",
method=HTTPMethod.GET,
headers={},
validations=[{"type": "equals", "field": "status", "value": "ok"}]
)
passed, error = validation_engine.validate_response(
test_case,
200,
{"status": "ok"},
{}
)
assert passed == True
def test_validate_equals_failure(self, validation_engine):
"""测试相等验证失败"""
test_case = TestCase(
id="TC004",
name="测试相等",
description="测试相等验证",
module="test",
endpoint="/api/test",
method=HTTPMethod.GET,
headers={},
validations=[{"type": "equals", "field": "status", "value": "ok"}]
)
passed, error = validation_engine.validate_response(
test_case,
200,
{"status": "error"},
{}
)
assert passed == False
assert "相等验证失败" in error
def test_validate_json_path_success(self, validation_engine):
"""测试JSON路径验证成功"""
test_case = TestCase(
id="TC005",
name="测试JSON路径",
description="测试JSON路径验证",
module="test",
endpoint="/api/test",
method=HTTPMethod.GET,
headers={},
validations=[{"type": "json_path", "path": "data.user.name", "value": "John"}]
)
passed, error = validation_engine.validate_response(
test_case,
200,
{"data": {"user": {"name": "John"}}},
{}
)
assert passed == True
def test_validate_json_path_failure(self, validation_engine):
"""测试JSON路径验证失败"""
test_case = TestCase(
id="TC005",
name="测试JSON路径",
description="测试JSON路径验证",
module="test",
endpoint="/api/test",
method=HTTPMethod.GET,
headers={},
validations=[{"type": "json_path", "path": "data.user.name", "value": "Jane"}]
)
passed, error = validation_engine.validate_response(
test_case,
200,
{"data": {"user": {"name": "John"}}},
{}
)
assert passed == False
assert "JSON路径验证失败" in error
def test_validate_regex_success(self, validation_engine):
"""测试正则表达式验证成功"""
test_case = TestCase(
id="TC006",
name="测试正则表达式",
description="测试正则表达式验证",
module="test",
endpoint="/api/test",
method=HTTPMethod.GET,
headers={},
validations=[{"type": "regex", "field": "email", "pattern": r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"}]
)
passed, error = validation_engine.validate_response(
test_case,
200,
{"email": "test@example.com"},
{}
)
assert passed == True
def test_validate_regex_failure(self, validation_engine):
"""测试正则表达式验证失败"""
test_case = TestCase(
id="TC006",
name="测试正则表达式",
description="测试正则表达式验证",
module="test",
endpoint="/api/test",
method=HTTPMethod.GET,
headers={},
validations=[{"type": "regex", "field": "email", "pattern": r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"}]
)
passed, error = validation_engine.validate_response(
test_case,
200,
{"email": "invalid-email"},
{}
)
assert passed == False
assert "正则表达式验证失败" in error
def test_validate_header_success(self, validation_engine):
"""测试响应头验证成功"""
test_case = TestCase(
id="TC007",
name="测试响应头",
description="测试响应头验证",
module="test",
endpoint="/api/test",
method=HTTPMethod.GET,
headers={},
validations=[{"type": "header", "name": "Content-Type", "value": "application/json"}]
)
passed, error = validation_engine.validate_response(
test_case,
200,
{},
{"Content-Type": "application/json"}
)
assert passed == True
def test_validate_header_not_found(self, validation_engine):
"""测试响应头不存在"""
test_case = TestCase(
id="TC007",
name="测试响应头",
description="测试响应头验证",
module="test",
endpoint="/api/test",
method=HTTPMethod.GET,
headers={},
validations=[{"type": "header", "name": "X-Custom-Header"}]
)
passed, error = validation_engine.validate_response(
test_case,
200,
{},
{"Content-Type": "application/json"}
)
assert passed == False
assert "响应头中未找到" in error
def test_validate_schema_success(self, validation_engine):
"""测试结构验证成功"""
test_case = TestCase(
id="TC008",
name="测试结构",
description="测试结构验证",
module="test",
endpoint="/api/test",
method=HTTPMethod.GET,
headers={},
validations=[{"type": "schema", "schema": {"name": "str", "age": "int"}}]
)
passed, error = validation_engine.validate_response(
test_case,
200,
{"name": "John", "age": 30},
{}
)
assert passed == True
def test_validate_schema_failure(self, validation_engine):
"""测试结构验证失败"""
test_case = TestCase(
id="TC008",
name="测试结构",
description="测试结构验证",
module="test",
endpoint="/api/test",
method=HTTPMethod.GET,
headers={},
validations=[{"type": "schema", "schema": {"name": "str", "age": "int"}}]
)
passed, error = validation_engine.validate_response(
test_case,
200,
{"name": "John", "age": "thirty"},
{}
)
assert passed == False
assert "字段 age 类型错误" in error
def test_validate_performance_success(self, validation_engine):
"""测试性能验证成功"""
from datetime import datetime
performance = PerformanceMetrics(
timestamp=datetime.now(),
response_time=1000,
request_size=100,
response_size=200
)
passed, error = validation_engine.validate_performance(performance, 5000)
assert passed == True
assert error == ""
def test_validate_performance_failure(self, validation_engine):
"""测试性能验证失败"""
from datetime import datetime
performance = PerformanceMetrics(
timestamp=datetime.now(),
response_time=6000,
request_size=100,
response_size=200
)
passed, error = validation_engine.validate_performance(performance, 5000)
assert passed == False
assert "响应时间超过阈值" in error
def test_validate_multiple_validations(self, validation_engine):
"""测试多个验证规则"""
test_case = TestCase(
id="TC009",
name="测试多验证",
description="测试多个验证规则",
module="test",
endpoint="/api/test",
method=HTTPMethod.GET,
headers={},
validations=[
{"type": "status_code", "value": 200},
{"type": "contains", "value": "success"},
{"type": "equals", "field": "status", "value": "ok"}
]
)
passed, error = validation_engine.validate_response(
test_case,
200,
{"status": "ok", "message": "operation success"},
{}
)
assert passed == True
def test_validate_multiple_validations_failure(self, validation_engine):
"""测试多个验证规则(其中一个失败)"""
test_case = TestCase(
id="TC009",
name="测试多验证",
description="测试多个验证规则",
module="test",
endpoint="/api/test",
method=HTTPMethod.GET,
headers={},
validations=[
{"type": "status_code", "value": 200},
{"type": "contains", "value": "error"},
{"type": "equals", "field": "status", "value": "ok"}
]
)
passed, error = validation_engine.validate_response(
test_case,
200,
{"status": "ok", "message": "operation success"},
{}
)
assert passed == False
assert "包含验证失败" in error
def test_validate_no_validations(self, validation_engine):
"""测试无验证规则"""
test_case = TestCase(
id="TC010",
name="测试无验证",
description="测试无验证规则",
module="test",
endpoint="/api/test",
method=HTTPMethod.GET,
headers={}
)
passed, error = validation_engine.validate_response(
test_case,
200,
{"message": "success"},
{}
)
assert passed == True
assert error == ""
def test_validate_unsupported_type(self, validation_engine):
"""测试不支持的验证类型"""
test_case = TestCase(
id="TC011",
name="测试不支持的类型",
description="测试不支持的验证类型",
module="test",
endpoint="/api/test",
method=HTTPMethod.GET,
headers={},
validations=[{"type": "unsupported_type"}]
)
passed, error = validation_engine.validate_response(
test_case,
200,
{"message": "success"},
{}
)
assert passed == False
assert "不支持的验证类型" in error