zhouhui.jiang

接口API测试

1 +{
2 + "token": "eyJhbGciOiJIUzUxMiJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwic3ViIjoiYWRtaW4iLCJpYXQiOjE3NjAwMDM1NjMsImV4cCI6MTc2MDA4OTk2M30.KnogGaxl7mYGJTZnXk0tY8ohvNHy8jcR2mMd7p_Nz_iMqSYngtDJa4t84XuSIeaj6uhBGRXi1Mamufo2Nf8BYQ",
3 + "saved_at": 1760003563
4 +}
...\ No newline at end of file ...\ No newline at end of file
1 +# API 测试脚本
2 +
3 +基于 Python 3.7.8 的 Apple ERP 系统 API 接口测试脚本。
4 +
5 +## 环境要求
6 +
7 +- Python 3.7.8+
8 +- requests 库
9 +
10 +```bash
11 +pip install requests
12 +```
13 +
14 +## 配置
15 +
16 +- 默认API基地址:`http://localhost:8083`
17 +- 默认登录账号:`admin`
18 +- 默认登录密码:`password`
19 +
20 +可通过环境变量 `API_BASE_URL` 修改基地址:
21 +```bash
22 +export API_BASE_URL=http://your-server:8083
23 +```
24 +
25 +## 使用方法
26 +
27 +### 1. 单独运行测试
28 +
29 +```bash
30 +# 登录测试(获取并保存token)
31 +python test_auth.py
32 +
33 +# 用户管理测试
34 +python test_users.py
35 +
36 +# 角色管理测试
37 +python test_roles.py
38 +
39 +# 菜单管理测试
40 +python test_menus.py
41 +
42 +# 字典管理测试
43 +python test_dicts.py
44 +```
45 +
46 +### 2. 一键运行所有测试
47 +
48 +```bash
49 +python run_all_tests.py
50 +```
51 +
52 +### 3. 自定义登录账号
53 +
54 +```bash
55 +python test_auth.py your_username your_password
56 +```
57 +
58 +## 测试内容
59 +
60 +### 认证管理 (test_auth.py)
61 +- 用户登录
62 +- 获取并保存JWT token
63 +- 打印token供后续使用
64 +
65 +### 用户管理 (test_users.py)
66 +- 获取用户列表
67 +- 新增用户(含模拟数据)
68 +- 编辑用户信息
69 +- 重置用户密码
70 +- 修改用户状态
71 +- 删除用户
72 +
73 +### 角色管理 (test_roles.py)
74 +- 获取角色列表
75 +- 新增角色(含模拟数据)
76 +- 编辑角色信息
77 +- 修改角色状态
78 +- 删除角色
79 +
80 +### 菜单管理 (test_menus.py)
81 +- 获取菜单列表(占位实现,需根据实际接口调整)
82 +
83 +### 字典管理 (test_dicts.py)
84 +- 获取字典列表(占位实现,需根据实际接口调整)
85 +
86 +## 注意事项
87 +
88 +1. **Token管理**:登录成功后,token会自动保存到 `.token.json` 文件中,后续请求会自动携带。
89 +
90 +2. **模拟数据**:用户和角色测试使用随机生成的模拟数据,避免与现有数据冲突。
91 +
92 +3. **菜单和字典**:由于API文档中未找到明确的接口定义,当前为占位实现。如有具体接口路径,请修改对应脚本。
93 +
94 +4. **错误处理**:脚本包含基本的错误处理和状态码检查。
95 +
96 +## 文件说明
97 +
98 +- `client.py` - API客户端封装类
99 +- `test_auth.py` - 认证登录测试
100 +- `test_users.py` - 用户管理测试
101 +- `test_roles.py` - 角色管理测试
102 +- `test_menus.py` - 菜单管理测试(占位)
103 +- `test_dicts.py` - 字典管理测试(占位)
104 +- `run_all_tests.py` - 一键运行所有测试
105 +- `.token.json` - 自动生成的token存储文件(运行后产生)
106 +
107 +## 扩展
108 +
109 +如需添加更多接口测试,可参考现有脚本结构:
110 +
111 +1. 创建新的测试文件
112 +2. 使用 `ApiClient` 类进行请求
113 +3. 添加模拟数据生成
114 +4. 包含错误处理和状态检查
1 +"""API tests package (Python 3.7.8 compatible)."""
2 +
3 +
No preview for this file type
1 +import json
2 +import os
3 +import time
4 +from typing import Dict, Optional
5 +
6 +import requests
7 +
8 +
9 +class ApiClient:
10 +
11 + def __init__(self, base_url: str):
12 + self.base_url = base_url.rstrip('/')
13 + self.session = requests.Session()
14 + self.token_path = os.path.join(os.path.dirname(__file__), '.token.json')
15 + self.token: Optional[str] = None
16 + self._load_token()
17 +
18 + def _load_token(self) -> None:
19 + if os.path.exists(self.token_path):
20 + try:
21 + with open(self.token_path, 'r', encoding='utf-8') as f:
22 + data = json.load(f)
23 + self.token = data.get('token')
24 + except Exception:
25 + self.token = None
26 +
27 + def _save_token(self, token: str) -> None:
28 + self.token = token
29 + data = {
30 + 'token': token,
31 + 'saved_at': int(time.time())
32 + }
33 + with open(self.token_path, 'w', encoding='utf-8') as f:
34 + json.dump(data, f, ensure_ascii=False, indent=2)
35 +
36 + def set_token(self, token: str) -> None:
37 + self._save_token(token)
38 +
39 + def _headers(self, extra: Optional[Dict[str, str]] = None) -> Dict[str, str]:
40 + headers: Dict[str, str] = {
41 + 'Content-Type': 'application/json'
42 + }
43 + if self.token:
44 + headers['Authorization'] = f'Bearer {self.token}'
45 + if extra:
46 + headers.update(extra)
47 + return headers
48 +
49 + def post(self, path: str, json_body: Dict, auth: bool = True) -> requests.Response:
50 + url = f"{self.base_url}{path}"
51 + headers = self._headers() if auth else {'Content-Type': 'application/json'}
52 + return self.session.post(url, headers=headers, json=json_body, timeout=30)
53 +
54 + def get(self, path: str, params: Optional[Dict] = None, auth: bool = True) -> requests.Response:
55 + url = f"{self.base_url}{path}"
56 + headers = self._headers() if auth else {}
57 + return self.session.get(url, headers=headers, params=params or {}, timeout=30)
58 +
59 + def put(self, path: str, json_body: Dict, auth: bool = True) -> requests.Response:
60 + url = f"{self.base_url}{path}"
61 + headers = self._headers() if auth else {'Content-Type': 'application/json'}
62 + return self.session.put(url, headers=headers, json=json_body, timeout=30)
63 +
64 + def delete(self, path: str, auth: bool = True) -> requests.Response:
65 + url = f"{self.base_url}{path}"
66 + headers = self._headers() if auth else {}
67 + return self.session.delete(url, headers=headers, timeout=30)
68 +
69 +
1 +#!/usr/bin/env python3
2 +# -*- coding: utf-8 -*-
3 +"""
4 +运行所有API测试脚本
5 +Python 3.7.8 兼容
6 +"""
7 +
8 +import os
9 +import sys
10 +import subprocess
11 +from typing import List, Tuple
12 +
13 +
14 +def run_script(script_path: str, args: List[str] = None) -> Tuple[bool, str]:
15 + """运行单个测试脚本"""
16 + cmd = [sys.executable, script_path]
17 + if args:
18 + cmd.extend(args)
19 +
20 + try:
21 + result = subprocess.run(
22 + cmd,
23 + capture_output=True,
24 + text=True,
25 + cwd=os.path.dirname(__file__)
26 + )
27 + return result.returncode == 0, result.stdout + result.stderr
28 + except Exception as e:
29 + return False, str(e)
30 +
31 +
32 +def main():
33 + """主函数:按顺序运行所有测试"""
34 + print("=" * 60)
35 + print("Apple ERP API 测试套件")
36 + print("=" * 60)
37 +
38 + # 测试脚本列表(按执行顺序)
39 + tests = [
40 + ("认证登录测试", "test_auth.py", ["admin", "password"]),
41 + ("用户管理测试", "test_users.py", []),
42 + ("角色管理测试", "test_roles.py", []),
43 + ("菜单管理测试", "test_menus.py", []),
44 + ("字典管理测试", "test_dicts.py", []),
45 + ]
46 +
47 + success_count = 0
48 + total_count = len(tests)
49 +
50 + for test_name, script_name, args in tests:
51 + print(f"\n{'='*20} {test_name} {'='*20}")
52 + script_path = os.path.join(os.path.dirname(__file__), script_name)
53 +
54 + if not os.path.exists(script_path):
55 + print(f"❌ 脚本不存在: {script_path}")
56 + continue
57 +
58 + success, output = run_script(script_path, args)
59 +
60 + if success:
61 + print(f"✅ {test_name} 执行成功")
62 + success_count += 1
63 + else:
64 + print(f"❌ {test_name} 执行失败")
65 +
66 + # 显示输出(截取前500字符避免过长)
67 + if output:
68 + print("输出:")
69 + print(output[:500] + ("..." if len(output) > 500 else ""))
70 +
71 + # 总结
72 + print("\n" + "=" * 60)
73 + print(f"测试完成: {success_count}/{total_count} 个测试通过")
74 + print("=" * 60)
75 +
76 + if success_count == total_count:
77 + print("🎉 所有测试都通过了!")
78 + return 0
79 + else:
80 + print("⚠️ 部分测试失败,请检查输出信息")
81 + return 1
82 +
83 +
84 +if __name__ == "__main__":
85 + sys.exit(main())
1 +import os
2 +import sys
3 +from typing import Tuple
4 +
5 +from client import ApiClient
6 +
7 +
8 +BASE_URL = os.environ.get('API_BASE_URL', 'http://localhost:8083')
9 +
10 +
11 +def login_and_print_token(username: str, password: str) -> Tuple[bool, str]:
12 + client = ApiClient(BASE_URL)
13 + resp = client.post('/api/auth/login', {
14 + 'username': username,
15 + 'password': password
16 + }, auth=False)
17 + try:
18 + data = resp.json()
19 + except Exception:
20 + print('登录接口返回非JSON,status=', resp.status_code)
21 + print(resp.text)
22 + return False, ''
23 +
24 + if resp.status_code == 200 and isinstance(data, dict) and data.get('code') == 200:
25 + token = (data.get('data') or {}).get('token')
26 + if token:
27 + print('登录成功,token=')
28 + print(token)
29 + client.set_token(token)
30 + return True, token
31 + print('登录成功但未返回token,完整返回:', data)
32 + return False, ''
33 +
34 + print('登录失败:', data)
35 + return False, ''
36 +
37 +
38 +if __name__ == '__main__':
39 + # 默认账户可根据实际系统修改
40 + username = sys.argv[1] if len(sys.argv) > 1 else 'admin'
41 + password = sys.argv[2] if len(sys.argv) > 2 else 'password'
42 + success, _ = login_and_print_token(username, password)
43 + sys.exit(0 if success else 1)
44 +
45 +
1 +from client import ApiClient
2 +
3 +
4 +def run_dict_tests(base_url: str = 'http://localhost:8083') -> None:
5 + client = ApiClient(base_url)
6 + # 文档未找到明确的字典接口路径,这里提供占位尝试
7 + possible_list = ['/api/system/dict/list', '/api/dict/list']
8 + for path in possible_list:
9 + resp = client.get(path)
10 + print('字典列表(尝试路径=', path, ') status=', resp.status_code)
11 + print(resp.text)
12 + if resp.status_code == 200:
13 + break
14 +
15 +
16 +if __name__ == '__main__':
17 + run_dict_tests()
18 +
19 +
1 +from client import ApiClient
2 +
3 +
4 +def run_menu_tests(base_url: str = 'http://localhost:8083') -> None:
5 + client = ApiClient(base_url)
6 + # 文档未提供菜单接口明确定义,这里给出占位:如有具体端点,请替换下列路径
7 + # 示例:GET 列表、POST 新增、POST 编辑、DELETE 删除
8 + possible_list = ['/api/system/menu/list', '/api/menu/list']
9 + for path in possible_list:
10 + resp = client.get(path)
11 + print('菜单列表(尝试路径=', path, ') status=', resp.status_code)
12 + print(resp.text)
13 + if resp.status_code == 200:
14 + break
15 +
16 +
17 +if __name__ == '__main__':
18 + run_menu_tests()
19 +
20 +
1 +import random
2 +import string
3 +from typing import Dict
4 +
5 +from client import ApiClient
6 +
7 +
8 +def _rand_suffix(n: int = 5) -> str:
9 + import random as _r, string as _s
10 + return ''.join(_r.choice(_s.ascii_lowercase + _s.digits) for _ in range(n))
11 +
12 +
13 +def run_role_tests(base_url: str = 'http://localhost:8083') -> None:
14 + client = ApiClient(base_url)
15 +
16 + # 1) 角色列表
17 + resp = client.get('/api/system/role/list', params={'pageNum': 1, 'pageSize': 5})
18 + print('角色列表 status=', resp.status_code)
19 + print(resp.text)
20 +
21 + # 2) 新增角色
22 + new_role: Dict = {
23 + 'roleCode': f'test_{_rand_suffix()}',
24 + 'roleName': '接口测试角色',
25 + 'status': 1,
26 + 'menuIds': [] # 如有菜单ID可填充
27 + }
28 + resp = client.post('/api/system/role/add', new_role)
29 + print('新增角色 status=', resp.status_code)
30 + print(resp.text)
31 +
32 + role_id = None
33 + try:
34 + lst = client.get('/api/system/role/list', params={'roleCode': new_role['roleCode'], 'pageNum': 1, 'pageSize': 1}).json()
35 + records = ((lst.get('data') or {}).get('records') or [])
36 + if records:
37 + role_id = records[0].get('roleId')
38 + except Exception:
39 + pass
40 +
41 + # 3) 编辑角色
42 + if role_id:
43 + edit_body = {
44 + 'roleId': role_id,
45 + 'roleCode': new_role['roleCode'],
46 + 'roleName': '接口测试角色-已修改',
47 + 'status': 1,
48 + 'menuIds': []
49 + }
50 + resp = client.post('/api/system/role/edit', edit_body)
51 + print('编辑角色 status=', resp.status_code)
52 + print(resp.text)
53 +
54 + # 4) 停用角色
55 + resp = client.post('/api/system/role/changeStatus', {'roleId': role_id, 'status': 0})
56 + print('修改角色状态 status=', resp.status_code)
57 + print(resp.text)
58 +
59 + # 5) 删除角色
60 + resp = client.delete(f'/api/system/role/{role_id}')
61 + print('删除角色 status=', resp.status_code)
62 + print(resp.text)
63 +
64 +
65 +if __name__ == '__main__':
66 + run_role_tests()
67 +
68 +
1 +import random
2 +import string
3 +from typing import Dict
4 +
5 +from client import ApiClient
6 +
7 +
8 +def _rand_suffix(n: int = 6) -> str:
9 + return ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(n))
10 +
11 +
12 +def run_user_tests(base_url: str = 'http://localhost:8083') -> None:
13 + client = ApiClient(base_url)
14 +
15 + # 1) 列表查询
16 + resp = client.get('/api/system/user/list', params={'pageNum': 1, 'pageSize': 5})
17 + print('用户列表 status=', resp.status_code)
18 + print(resp.text)
19 +
20 + # 2) 新增用户
21 + mock_user: Dict = {
22 + 'username': f'test_{_rand_suffix()}',
23 + 'password': '123456',
24 + 'realName': '接口测试用户',
25 + 'phone': '18221643976',
26 + 'email': f'test_{_rand_suffix()}@example.com',
27 + 'status': 1,
28 + 'roleIds': [1] # 根据系统内置角色调整
29 + }
30 + resp = client.post('/api/system/user/add', mock_user)
31 + print('新增用户 status=', resp.status_code)
32 + print(resp.text)
33 +
34 + user_id = None
35 + try:
36 + data = resp.json()
37 + if data.get('code') == 200:
38 + # 简化:再拉一次列表找刚才的用户
39 + lst = client.get('/api/system/user/list', params={'username': mock_user['username'], 'pageNum': 1, 'pageSize': 1}).json()
40 + records = ((lst.get('data') or {}).get('records') or [])
41 + if records:
42 + user_id = records[0].get('userId')
43 + except Exception:
44 + pass
45 +
46 + # 3) 编辑用户(若拿到了ID)
47 + if user_id:
48 + edit_body = {
49 + 'userId': user_id,
50 + 'username': mock_user['username'],
51 + 'realName': '接口测试用户-已修改',
52 + 'phone': mock_user['phone'],
53 + 'email': mock_user['email'],
54 + 'status': 1,
55 + 'roleIds': [1]
56 + }
57 + resp = client.post('/api/system/user/edit', edit_body)
58 + print('编辑用户 status=', resp.status_code)
59 + print(resp.text)
60 +
61 + # 4) 重置密码
62 + resp = client.put('/api/system/user/resetPwd', {'userId': user_id, 'newPassword': 'newpass123'})
63 + print('重置密码 status=', resp.status_code)
64 + print(resp.text)
65 +
66 + # 5) 停用用户
67 + resp = client.post('/api/system/user/changeStatus', {'userId': user_id, 'status': 0})
68 + print('修改用户状态 status=', resp.status_code)
69 + print(resp.text)
70 +
71 + # 6) 删除用户
72 + resp = client.delete(f'/api/system/user/{user_id}')
73 + print('删除用户 status=', resp.status_code)
74 + print(resp.text)
75 +
76 +
77 +if __name__ == '__main__':
78 + run_user_tests()
79 +
80 +