08ea5fbe98
添加用户管理视图、API和状态管理文件
711 lines
20 KiB
Python
711 lines
20 KiB
Python
"""
|
|
API测试用例模块(增强版)
|
|
支持参数化测试、边界条件测试和异常场景测试
|
|
"""
|
|
|
|
from typing import List, Dict, Any, Callable
|
|
from functools import wraps
|
|
import pytest
|
|
|
|
from core.api_tester import APITester
|
|
from core.validation import ValidationRule
|
|
from data.test_data import TestDataGenerator
|
|
|
|
|
|
def ensure_auth(test_func: Callable) -> Callable:
|
|
"""
|
|
装饰器:确保测试前已认证
|
|
|
|
Args:
|
|
test_func: 测试函数
|
|
|
|
Returns:
|
|
包装后的测试函数
|
|
"""
|
|
@wraps(test_func)
|
|
def wrapper(self, *args, **kwargs):
|
|
# 确保已认证
|
|
if not self.tester._ensure_authenticated():
|
|
return False
|
|
return test_func(self, *args, **kwargs)
|
|
return wrapper
|
|
|
|
|
|
class APITestCases:
|
|
"""API测试用例集合(增强版)"""
|
|
|
|
def __init__(self, tester: APITester):
|
|
"""
|
|
初始化测试用例
|
|
|
|
Args:
|
|
tester: API测试器实例
|
|
"""
|
|
self.tester = tester
|
|
self.test_data_generator = TestDataGenerator()
|
|
self.created_user_id = None
|
|
self.created_role_id = None
|
|
|
|
@ensure_auth
|
|
def test_health_check(self) -> bool:
|
|
"""
|
|
测试健康检查接口
|
|
|
|
Returns:
|
|
是否通过
|
|
"""
|
|
result = self.tester.request(
|
|
"GET",
|
|
"/actuator/health",
|
|
test_name="健康检查",
|
|
expected_status=200,
|
|
require_auth=False
|
|
)
|
|
|
|
validation_rules = [
|
|
ValidationRule("status_code", 200),
|
|
ValidationRule("json_path", "UP", path="status")
|
|
]
|
|
|
|
result = self.tester.validate(result, validation_rules)
|
|
return result.passed
|
|
|
|
def test_login(self, username: str = None, password: str = None) -> bool:
|
|
"""
|
|
测试用户登录
|
|
|
|
Args:
|
|
username: 用户名
|
|
password: 密码
|
|
|
|
Returns:
|
|
是否通过
|
|
"""
|
|
username = username or "admin"
|
|
password = password or "admin123"
|
|
|
|
result = self.tester.request(
|
|
"POST",
|
|
"/auth/login",
|
|
data={"username": username, "password": password},
|
|
test_name="用户登录",
|
|
expected_status=200,
|
|
require_auth=False
|
|
)
|
|
|
|
validation_rules = [
|
|
ValidationRule("status_code", 200),
|
|
ValidationRule("json_path", 200, path="code"),
|
|
ValidationRule("json_path", "token", path="data.token")
|
|
]
|
|
|
|
result = self.tester.validate(result, validation_rules)
|
|
return result.passed
|
|
|
|
@ensure_auth
|
|
def test_get_user_list(self) -> bool:
|
|
"""
|
|
测试获取用户列表
|
|
|
|
Returns:
|
|
是否通过
|
|
"""
|
|
result = self.tester.request(
|
|
"GET",
|
|
"/user/list",
|
|
params={"page": 1, "pageSize": 10},
|
|
test_name="获取用户列表",
|
|
expected_status=200
|
|
)
|
|
|
|
validation_rules = [
|
|
ValidationRule("status_code", 200),
|
|
ValidationRule("json_path", 200, path="code"),
|
|
ValidationRule("json_path", "records", path="data")
|
|
]
|
|
|
|
result = self.tester.validate(result, validation_rules)
|
|
return result.passed
|
|
|
|
@ensure_auth
|
|
def test_get_role_list(self) -> bool:
|
|
"""
|
|
测试获取角色列表
|
|
|
|
Returns:
|
|
是否通过
|
|
"""
|
|
result = self.tester.request(
|
|
"GET",
|
|
"/role/list",
|
|
params={"page": 1, "pageSize": 10},
|
|
test_name="获取角色列表",
|
|
expected_status=200
|
|
)
|
|
|
|
validation_rules = [
|
|
ValidationRule("status_code", 200),
|
|
ValidationRule("json_path", 200, path="code"),
|
|
ValidationRule("json_path", "records", path="data")
|
|
]
|
|
|
|
result = self.tester.validate(result, validation_rules)
|
|
return result.passed
|
|
|
|
@ensure_auth
|
|
def test_get_menu_list(self) -> bool:
|
|
"""
|
|
测试获取菜单列表
|
|
|
|
Returns:
|
|
是否通过
|
|
"""
|
|
result = self.tester.request(
|
|
"GET",
|
|
"/menu/list",
|
|
test_name="获取菜单列表",
|
|
expected_status=200
|
|
)
|
|
|
|
validation_rules = [
|
|
ValidationRule("status_code", 200),
|
|
ValidationRule("json_path", 200, path="code"),
|
|
ValidationRule("json_path", "list", path="data")
|
|
]
|
|
|
|
result = self.tester.validate(result, validation_rules)
|
|
return result.passed
|
|
|
|
@ensure_auth
|
|
def test_create_user(self) -> bool:
|
|
"""
|
|
测试创建用户
|
|
|
|
Returns:
|
|
是否通过
|
|
"""
|
|
user_data = self.test_data_generator.generate_user_data()
|
|
|
|
result = self.tester.request(
|
|
"POST",
|
|
"/user/create",
|
|
data=user_data,
|
|
test_name="创建用户",
|
|
expected_status=200
|
|
)
|
|
|
|
validation_rules = [
|
|
ValidationRule("status_code", 200),
|
|
ValidationRule("json_path", 200, path="code"),
|
|
ValidationRule("json_path", user_data["username"], path="data.username")
|
|
]
|
|
|
|
result = self.tester.validate(result, validation_rules)
|
|
|
|
# 保存创建的用户ID
|
|
if result.passed and result.response_data.get("data"):
|
|
self.created_user_id = result.response_data["data"].get("id")
|
|
|
|
return result.passed
|
|
|
|
@ensure_auth
|
|
def test_create_role(self) -> bool:
|
|
"""
|
|
测试创建角色
|
|
|
|
Returns:
|
|
是否通过
|
|
"""
|
|
role_data = self.test_data_generator.generate_role_data()
|
|
|
|
result = self.tester.request(
|
|
"POST",
|
|
"/role/create",
|
|
data=role_data,
|
|
test_name="创建角色",
|
|
expected_status=200
|
|
)
|
|
|
|
validation_rules = [
|
|
ValidationRule("status_code", 200),
|
|
ValidationRule("json_path", 200, path="code"),
|
|
ValidationRule("json_path", role_data["roleName"], path="data.roleName")
|
|
]
|
|
|
|
result = self.tester.validate(result, validation_rules)
|
|
|
|
# 保存创建的角色ID
|
|
if result.passed and result.response_data.get("data"):
|
|
self.created_role_id = result.response_data["data"].get("id")
|
|
|
|
return result.passed
|
|
|
|
@ensure_auth
|
|
def test_update_user(self, user_id: int = None) -> bool:
|
|
"""
|
|
测试更新用户
|
|
|
|
Args:
|
|
user_id: 用户ID
|
|
|
|
Returns:
|
|
是否通过
|
|
"""
|
|
user_data = self.test_data_generator.generate_user_data()
|
|
|
|
if user_id:
|
|
user_data["id"] = user_id
|
|
elif self.created_user_id:
|
|
user_data["id"] = self.created_user_id
|
|
|
|
result = self.tester.request(
|
|
"PUT",
|
|
"/user/update",
|
|
data=user_data,
|
|
test_name="更新用户",
|
|
expected_status=200
|
|
)
|
|
|
|
validation_rules = [
|
|
ValidationRule("status_code", 200),
|
|
ValidationRule("json_path", 200, path="code")
|
|
]
|
|
|
|
result = self.tester.validate(result, validation_rules)
|
|
return result.passed
|
|
|
|
@ensure_auth
|
|
def test_delete_user(self, user_id: int = None) -> bool:
|
|
"""
|
|
测试删除用户
|
|
|
|
Args:
|
|
user_id: 用户ID
|
|
|
|
Returns:
|
|
是否通过
|
|
"""
|
|
if user_id is None:
|
|
user_id = self.created_user_id
|
|
|
|
if user_id is None:
|
|
return False
|
|
|
|
result = self.tester.request(
|
|
"DELETE",
|
|
f"/user/delete/{user_id}",
|
|
test_name="删除用户",
|
|
expected_status=200
|
|
)
|
|
|
|
validation_rules = [
|
|
ValidationRule("status_code", 200),
|
|
ValidationRule("json_path", 200, path="code")
|
|
]
|
|
|
|
result = self.tester.validate(result, validation_rules)
|
|
return result.passed
|
|
|
|
@ensure_auth
|
|
def test_get_user_info(self, user_id: int = None) -> bool:
|
|
"""
|
|
测试获取用户信息
|
|
|
|
Args:
|
|
user_id: 用户ID
|
|
|
|
Returns:
|
|
是否通过
|
|
"""
|
|
if user_id is None:
|
|
user_id = self.created_user_id
|
|
|
|
if user_id is None:
|
|
return False
|
|
|
|
result = self.tester.request(
|
|
"GET",
|
|
f"/user/info/{user_id}",
|
|
test_name="获取用户信息",
|
|
expected_status=200
|
|
)
|
|
|
|
validation_rules = [
|
|
ValidationRule("status_code", 200),
|
|
ValidationRule("json_path", 200, path="code"),
|
|
ValidationRule("json_path", user_id, path="data.id")
|
|
]
|
|
|
|
result = self.tester.validate(result, validation_rules)
|
|
return result.passed
|
|
|
|
@ensure_auth
|
|
def test_search_users(self, keyword: str = None) -> bool:
|
|
"""
|
|
测试搜索用户
|
|
|
|
Args:
|
|
keyword: 搜索关键词
|
|
|
|
Returns:
|
|
是否通过
|
|
"""
|
|
keyword = keyword or "admin"
|
|
|
|
result = self.tester.request(
|
|
"GET",
|
|
"/user/search",
|
|
params={"keyword": keyword, "page": 1, "pageSize": 10},
|
|
test_name="搜索用户",
|
|
expected_status=200
|
|
)
|
|
|
|
validation_rules = [
|
|
ValidationRule("status_code", 200),
|
|
ValidationRule("json_path", 200, path="code"),
|
|
ValidationRule("json_path", "records", path="data")
|
|
]
|
|
|
|
result = self.tester.validate(result, validation_rules)
|
|
return result.passed
|
|
|
|
def run_all_tests(self) -> Dict[str, bool]:
|
|
"""
|
|
运行所有基础测试用例
|
|
|
|
Returns:
|
|
测试结果字典
|
|
"""
|
|
results = {}
|
|
|
|
# 健康检查
|
|
results["health_check"] = self.test_health_check()
|
|
|
|
# 登录
|
|
results["login"] = self.test_login()
|
|
|
|
# 获取列表
|
|
results["get_user_list"] = self.test_get_user_list()
|
|
results["get_role_list"] = self.test_get_role_list()
|
|
results["get_menu_list"] = self.test_get_menu_list()
|
|
|
|
# 创建
|
|
results["create_user"] = self.test_create_user()
|
|
results["create_role"] = self.test_create_role()
|
|
|
|
# 搜索
|
|
results["search_users"] = self.test_search_users()
|
|
|
|
return results
|
|
|
|
|
|
class ParameterizedTestCases:
|
|
"""参数化测试用例"""
|
|
|
|
def __init__(self, tester: APITester):
|
|
"""
|
|
初始化参数化测试用例
|
|
|
|
Args:
|
|
tester: API测试器实例
|
|
"""
|
|
self.tester = tester
|
|
self.test_data_generator = TestDataGenerator()
|
|
|
|
@ensure_auth
|
|
def test_login_with_different_credentials(self, credentials: List[Dict[str, str]]) -> Dict[str, bool]:
|
|
"""
|
|
使用不同凭据测试登录
|
|
|
|
Args:
|
|
credentials: 凭据列表,每个元素包含username和password
|
|
|
|
Returns:
|
|
测试结果字典
|
|
"""
|
|
results = {}
|
|
|
|
for cred in credentials:
|
|
username = cred.get("username")
|
|
password = cred.get("password")
|
|
test_name = f"登录测试 - {username}"
|
|
|
|
result = self.tester.request(
|
|
"POST",
|
|
"/auth/login",
|
|
data={"username": username, "password": password},
|
|
test_name=test_name,
|
|
expected_status=200,
|
|
require_auth=False
|
|
)
|
|
|
|
validation_rules = [
|
|
ValidationRule("status_code", 200),
|
|
ValidationRule("json_path", 200, path="code")
|
|
]
|
|
|
|
result = self.tester.validate(result, validation_rules)
|
|
results[test_name] = result.passed
|
|
|
|
return results
|
|
|
|
@ensure_auth
|
|
def test_pagination(self, page_sizes: List[int]) -> Dict[str, bool]:
|
|
"""
|
|
测试分页功能
|
|
|
|
Args:
|
|
page_sizes: 页面大小列表
|
|
|
|
Returns:
|
|
测试结果字典
|
|
"""
|
|
results = {}
|
|
|
|
for page_size in page_sizes:
|
|
test_name = f"分页测试 - 页面大小: {page_size}"
|
|
|
|
result = self.tester.request(
|
|
"GET",
|
|
"/user/list",
|
|
params={"page": 1, "pageSize": page_size},
|
|
test_name=test_name,
|
|
expected_status=200
|
|
)
|
|
|
|
validation_rules = [
|
|
ValidationRule("status_code", 200),
|
|
ValidationRule("json_path", 200, path="code"),
|
|
ValidationRule("json_path", "records", path="data")
|
|
]
|
|
|
|
result = self.tester.validate(result, validation_rules)
|
|
results[test_name] = result.passed
|
|
|
|
return results
|
|
|
|
|
|
class BoundaryTestCases:
|
|
"""边界条件测试用例"""
|
|
|
|
def __init__(self, tester: APITester):
|
|
"""
|
|
初始化边界条件测试用例
|
|
|
|
Args:
|
|
tester: API测试器实例
|
|
"""
|
|
self.tester = tester
|
|
self.test_data_generator = TestDataGenerator()
|
|
|
|
@ensure_auth
|
|
def test_user_name_length(self) -> Dict[str, bool]:
|
|
"""
|
|
测试用户名长度边界
|
|
|
|
Returns:
|
|
测试结果字典
|
|
"""
|
|
results = {}
|
|
|
|
# 测试空用户名
|
|
result = self.tester.request(
|
|
"POST",
|
|
"/auth/login",
|
|
data={"username": "", "password": "admin123"},
|
|
test_name="登录测试 - 空用户名",
|
|
expected_status=400,
|
|
require_auth=False
|
|
)
|
|
results["空用户名"] = result.status_code == 400
|
|
|
|
# 测试超长用户名
|
|
long_username = "a" * 100
|
|
result = self.tester.request(
|
|
"POST",
|
|
"/auth/login",
|
|
data={"username": long_username, "password": "admin123"},
|
|
test_name="登录测试 - 超长用户名",
|
|
expected_status=400,
|
|
require_auth=False
|
|
)
|
|
results["超长用户名"] = result.status_code == 400
|
|
|
|
return results
|
|
|
|
@ensure_auth
|
|
def test_page_size_boundaries(self) -> Dict[str, bool]:
|
|
"""
|
|
测试页面大小边界
|
|
|
|
Returns:
|
|
测试结果字典
|
|
"""
|
|
results = {}
|
|
|
|
# 测试页面大小为0
|
|
result = self.tester.request(
|
|
"GET",
|
|
"/user/list",
|
|
params={"page": 1, "pageSize": 0},
|
|
test_name="分页测试 - 页面大小为0",
|
|
expected_status=400
|
|
)
|
|
results["页面大小为0"] = result.status_code == 400
|
|
|
|
# 测试页面大小为负数
|
|
result = self.tester.request(
|
|
"GET",
|
|
"/user/list",
|
|
params={"page": 1, "pageSize": -1},
|
|
test_name="分页测试 - 页面大小为负数",
|
|
expected_status=400
|
|
)
|
|
results["页面大小为负数"] = result.status_code == 400
|
|
|
|
# 测试超大页面大小
|
|
result = self.tester.request(
|
|
"GET",
|
|
"/user/list",
|
|
params={"page": 1, "pageSize": 10000},
|
|
test_name="分页测试 - 超大页面大小",
|
|
expected_status=200
|
|
)
|
|
results["超大页面大小"] = result.status_code == 200
|
|
|
|
return results
|
|
|
|
|
|
class ExceptionTestCases:
|
|
"""异常场景测试用例"""
|
|
|
|
def __init__(self, tester: APITester):
|
|
"""
|
|
初始化异常场景测试用例
|
|
|
|
Args:
|
|
tester: API测试器实例
|
|
"""
|
|
self.tester = tester
|
|
self.test_data_generator = TestDataGenerator()
|
|
|
|
@ensure_auth
|
|
def test_invalid_credentials(self) -> Dict[str, bool]:
|
|
"""
|
|
测试无效凭据
|
|
|
|
Returns:
|
|
测试结果字典
|
|
"""
|
|
results = {}
|
|
|
|
# 测试错误密码
|
|
result = self.tester.request(
|
|
"POST",
|
|
"/auth/login",
|
|
data={"username": "admin", "password": "wrongpassword"},
|
|
test_name="登录测试 - 错误密码",
|
|
expected_status=401,
|
|
require_auth=False
|
|
)
|
|
results["错误密码"] = result.status_code == 401
|
|
|
|
# 测试不存在的用户
|
|
result = self.tester.request(
|
|
"POST",
|
|
"/auth/login",
|
|
data={"username": "nonexistent", "password": "admin123"},
|
|
test_name="登录测试 - 不存在的用户",
|
|
expected_status=401,
|
|
require_auth=False
|
|
)
|
|
results["不存在的用户"] = result.status_code == 401
|
|
|
|
return results
|
|
|
|
@ensure_auth
|
|
def test_missing_required_fields(self) -> Dict[str, bool]:
|
|
"""
|
|
测试缺少必填字段
|
|
|
|
Returns:
|
|
测试结果字典
|
|
"""
|
|
results = {}
|
|
|
|
# 测试缺少用户名
|
|
result = self.tester.request(
|
|
"POST",
|
|
"/auth/login",
|
|
data={"password": "admin123"},
|
|
test_name="登录测试 - 缺少用户名",
|
|
expected_status=400,
|
|
require_auth=False
|
|
)
|
|
results["缺少用户名"] = result.status_code == 400
|
|
|
|
# 测试缺少密码
|
|
result = self.tester.request(
|
|
"POST",
|
|
"/auth/login",
|
|
data={"username": "admin"},
|
|
test_name="登录测试 - 缺少密码",
|
|
expected_status=400,
|
|
require_auth=False
|
|
)
|
|
results["缺少密码"] = result.status_code == 400
|
|
|
|
return results
|
|
|
|
@ensure_auth
|
|
def test_unauthorized_access(self) -> Dict[str, bool]:
|
|
"""
|
|
测试未授权访问
|
|
|
|
Returns:
|
|
测试结果字典
|
|
"""
|
|
results = {}
|
|
|
|
# 临时清除认证
|
|
self.tester.auth_manager.logout()
|
|
|
|
# 测试未授权访问用户列表
|
|
result = self.tester.request(
|
|
"GET",
|
|
"/user/list",
|
|
test_name="未授权访问 - 用户列表",
|
|
expected_status=401,
|
|
require_auth=False
|
|
)
|
|
results["未授权访问用户列表"] = result.status_code == 401
|
|
|
|
# 重新认证
|
|
self.tester._ensure_authenticated()
|
|
|
|
return results
|
|
|
|
@ensure_auth
|
|
def test_invalid_http_methods(self) -> Dict[str, bool]:
|
|
"""
|
|
测试无效的HTTP方法
|
|
|
|
Returns:
|
|
测试结果字典
|
|
"""
|
|
results = {}
|
|
|
|
# 测试不支持的HTTP方法
|
|
try:
|
|
result = self.tester.request(
|
|
"PATCH",
|
|
"/user/list",
|
|
test_name="无效HTTP方法 - PATCH",
|
|
expected_status=405
|
|
)
|
|
results["PATCH方法"] = result.status_code == 405
|
|
except:
|
|
results["PATCH方法"] = False
|
|
|
|
return results
|