diff --git a/README.md b/README.md index 3a8fc9d..51325e7 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,358 @@ # LoadDensity -A high‑performance load testing and automation tool. -It supports fast user spawning, flexible templates, and generates reports in multiple formats. -Designed to be cross‑platform and easy to integrate into your projects. - -- Load automation: Quickly set up and run load tests -- User templates: Simple configuration for reusable test users -- Load Density scripts: Define and execute repeatable scenarios -- Report generation: Export results in JSON, HTML, or XML -- High throughput: Thousands of requests per second -- Fast user spawning: Scale up test users instantly -- Multi‑test support: Run multiple tests on a single task -- Configurable test duration: Specify how long tests should run -- OS independent: Works across major operating systems -- Remote automation: Execute tests remotely -- Project & template support: Organize and reuse test setup + +[![Python](https://img.shields.io/pypi/pyversions/je_load_density)](https://pypi.org/project/je_load_density/) +[![PyPI](https://img.shields.io/pypi/v/je_load_density)](https://pypi.org/project/je_load_density/) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) +[![Documentation](https://readthedocs.org/projects/loaddensity/badge/?version=latest)](https://loaddensity.readthedocs.io/en/latest/) + +**LoadDensity** is a high-performance load & stress testing automation framework built on top of [Locust](https://locust.io/). It provides a simplified wrapper around Locust's core functionality, enabling fast user spawning, flexible test configuration via templates and JSON-driven scripts, report generation in multiple formats (HTML / JSON / XML), a built-in GUI, remote execution via TCP socket server, and a callback mechanism for post-test workflows. + +**[繁體中文](README/README_zh-TW.md)** | **[简体中文](README/README_zh-CN.md)** + +--- + +## Features + +- **Simplified Locust Wrapper** — Abstracts Locust's `Environment`, `Runner`, and `User` classes behind a clean, high-level API. +- **Two User Types** — Supports both `HttpUser` and `FastHttpUser` (geventhttpclient-based, higher throughput). +- **Fast User Spawning** — Scale to thousands of concurrent users with configurable spawn rate. +- **JSON-Driven Test Scripts** — Define test scenarios as JSON files and execute them without writing Python code. +- **Action Executor** — A built-in event-driven executor that maps action names to functions. Supports batch execution and file-driven execution. +- **Report Generation** — Export test results in three formats: + - **HTML** — Styled tables with success/failure records + - **JSON** — Structured data for programmatic consumption + - **XML** — Standard XML output for CI/CD integration +- **Request Hook** — Automatically records every request (success and failure) with method, URL, status code, response body, headers, and errors. +- **Callback Executor** — Chain a trigger function with a callback function for post-test workflows (e.g., run test then generate report). +- **TCP Socket Server** — Remote execution server based on gevent. Accepts JSON commands over TCP to execute tests remotely. +- **Project Scaffolding** — Auto-generate project directory structure with keyword templates and executor scripts. +- **Package Manager** — Dynamically load external Python packages and register their functions into the executor at runtime. +- **GUI (Optional)** — PySide6-based graphical interface with real-time log display, supporting English and Traditional Chinese. +- **CLI Support** — Run tests, execute scripts, or scaffold projects directly from the command line. +- **Cross-Platform** — Works on Windows, macOS, and Linux. ## Installation +### Basic (CLI & Library) + +```bash +pip install je_load_density +``` + +### With GUI Support + +```bash +pip install je_load_density[gui] +``` + +This installs [PySide6](https://doc.qt.io/qtforpython/) and [qt-material](https://github.com/UN-GCPDS/qt-material) for the graphical interface. + +## Requirements + +- Python **3.10** or later +- [Locust](https://locust.io/) (installed automatically as a dependency) + +## Quick Start + +### 1. Using the Python API + +```python +from je_load_density import start_test + +# Define user configuration and tasks +result = start_test( + user_detail_dict={"user": "fast_http_user"}, + user_count=50, + spawn_rate=10, + test_time=10, + tasks={ + "get": {"request_url": "http://httpbin.org/get"}, + "post": {"request_url": "http://httpbin.org/post"}, + } +) +``` + +**Parameters:** +| Parameter | Type | Default | Description | +|---|---|---|---| +| `user_detail_dict` | `dict` | — | User type configuration. `{"user": "fast_http_user"}` or `{"user": "http_user"}` | +| `user_count` | `int` | `50` | Total number of simulated users | +| `spawn_rate` | `int` | `10` | Number of users spawned per second | +| `test_time` | `int` | `60` | Test duration in seconds. `None` for unlimited | +| `web_ui_dict` | `dict` | `None` | Enable Locust Web UI, e.g. `{"host": "127.0.0.1", "port": 8089}` | +| `tasks` | `dict` | — | HTTP method to request URL mapping | + +### 2. Using JSON Script Files + +Create a JSON file (`test_scenario.json`): + +```json +[ + ["LD_start_test", { + "user_detail_dict": {"user": "fast_http_user"}, + "user_count": 50, + "spawn_rate": 10, + "test_time": 5, + "tasks": { + "get": {"request_url": "http://httpbin.org/get"}, + "post": {"request_url": "http://httpbin.org/post"} + } + }] +] +``` + +Execute from Python: + +```python +from je_load_density import execute_action, read_action_json + +execute_action(read_action_json("test_scenario.json")) +``` + +### 3. Using the CLI + +```bash +# Execute a single JSON script file +python -m je_load_density -e test_scenario.json + +# Execute all JSON files in a directory +python -m je_load_density -d ./test_scripts/ + +# Execute an inline JSON string +python -m je_load_density --execute_str '[["LD_start_test", {"user_detail_dict": {"user": "fast_http_user"}, "user_count": 10, "spawn_rate": 5, "test_time": 5, "tasks": {"get": {"request_url": "http://httpbin.org/get"}}}]]' + +# Scaffold a new project with templates +python -m je_load_density -c MyProject +``` + +### 4. Using the GUI + +```python +from je_load_density.gui.main_window import LoadDensityUI +from PySide6.QtWidgets import QApplication +import sys + +app = QApplication(sys.argv) +window = LoadDensityUI() +window.show() +sys.exit(app.exec()) +``` + +## Report Generation + +After running a test, generate reports from the recorded data: + +```python +from je_load_density import ( + generate_html_report, + generate_json_report, + generate_xml_report, +) + +# HTML report — creates "my_report.html" +generate_html_report("my_report") + +# JSON report — creates "my_report_success.json" and "my_report_failure.json" +generate_json_report("my_report") + +# XML report — creates "my_report_success.xml" and "my_report_failure.xml" +generate_xml_report("my_report") +``` + +## Advanced Usage + +### Action Executor + +The executor maps string action names to callable functions. All built-in Python functions are also available. + +```python +from je_load_density import executor, add_command_to_executor + +# Register a custom function +def my_custom_action(message): + print(f"Custom: {message}") + +add_command_to_executor({"my_action": my_custom_action}) + +# Execute actions programmatically +executor.execute_action([ + ["my_action", ["Hello World"]], + ["print", ["Test complete"]], +]) +``` + +**Built-in executor actions:** +| Action Name | Description | +|---|---| +| `LD_start_test` | Start a load test | +| `LD_generate_html` | Generate HTML fragments | +| `LD_generate_html_report` | Generate full HTML report file | +| `LD_generate_json` | Generate JSON data structure | +| `LD_generate_json_report` | Generate JSON report files | +| `LD_generate_xml` | Generate XML strings | +| `LD_generate_xml_report` | Generate XML report files | +| `LD_execute_action` | Execute a list of actions | +| `LD_execute_files` | Execute actions from multiple files | +| `LD_add_package_to_executor` | Dynamically load a package into the executor | + +### Callback Executor + +Chain a trigger function with a callback: + +```python +from je_load_density import callback_executor + +def after_test(): + print("Test finished, generating report...") + +callback_executor.callback_function( + trigger_function_name="user_test", + callback_function=after_test, + user_detail_dict={"user": "fast_http_user"}, + user_count=10, + spawn_rate=5, + test_time=5, + tasks={"get": {"request_url": "http://httpbin.org/get"}}, +) +``` + +### TCP Socket Server (Remote Execution) + +Start a TCP server that accepts JSON commands: + +```python +from je_load_density import start_load_density_socket_server + +# Start server (blocking) +start_load_density_socket_server(host="localhost", port=9940) +``` + +Send commands from a client: + +```python +import socket, json + +sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +sock.connect(("localhost", 9940)) + +command = json.dumps([ + ["LD_start_test", { + "user_detail_dict": {"user": "fast_http_user"}, + "user_count": 10, "spawn_rate": 5, "test_time": 5, + "tasks": {"get": {"request_url": "http://httpbin.org/get"}} + }] +]) +sock.send(command.encode("utf-8")) +response = sock.recv(8192) +print(response.decode("utf-8")) +sock.close() +``` + +Send `"quit_server"` to gracefully shut down the server. + +### Project Scaffolding + +Generate a project with keyword templates and executor scripts: + +```python +from je_load_density import create_project_dir + +create_project_dir(project_path="./my_tests", parent_name="LoadDensity") +``` + +This creates: +``` +my_tests/ +└── LoadDensity/ + ├── keyword/ + │ ├── keyword1.json # FastHttpUser test template + │ └── keyword2.json # HttpUser test template + └── executor/ + ├── executor_one_file.py # Execute single keyword file + └── executor_folder.py # Execute all files in keyword/ ``` -pip install je_locust_wrapper + +### Dynamic Package Loading + +Load external packages and register their functions into the executor: + +```python +from je_load_density import executor + +# Load a package and make its functions available as executor actions +executor.execute_action([ + ["LD_add_package_to_executor", ["my_custom_package"]] +]) +``` + +### Test Records + +Access raw test records programmatically: + +```python +from je_load_density import test_record_instance + +# After running a test +for record in test_record_instance.test_record_list: + print(record["Method"], record["test_url"], record["status_code"]) + +for error in test_record_instance.error_record_list: + print(error["Method"], error["test_url"], error["error"]) + +# Clear records +test_record_instance.clear_records() ``` -## Require +## Architecture ``` -python 3.9 or later +je_load_density/ +├── __init__.py # Public API exports +├── __main__.py # CLI entry point +├── gui/ # PySide6 GUI (optional dependency) +│ ├── main_window.py # Main window (QMainWindow) +│ ├── main_widget.py # Test parameter form & log panel +│ ├── load_density_gui_thread.py # Background thread for tests +│ ├── log_to_ui_filter.py # Log interceptor for GUI display +│ └── language_wrapper/ # i18n (English, Traditional Chinese) +├── wrapper/ +│ ├── create_locust_env/ # Locust Environment & Runner setup +│ ├── start_wrapper/ # High-level start_test() entry point +│ ├── user_template/ # HttpUser & FastHttpUser wrappers +│ ├── proxy/ # User proxy container & configuration +│ └── event/ # Request hook (records all requests) +└── utils/ + ├── executor/ # Action executor (event-driven) + ├── generate_report/ # HTML, JSON, XML report generators + ├── test_record/ # Test record storage + ├── socket_server/ # TCP server for remote execution + ├── callback/ # Callback function executor + ├── project/ # Project scaffolding & templates + ├── package_manager/ # Dynamic package loading + ├── json/ # JSON file read/write utilities + ├── xml/ # XML structure utilities + ├── file_process/ # Directory file listing + ├── logging/ # Logger instance + └── exception/ # Custom exceptions & error tags ``` ## Tested Platforms -- Windows 10 ~ 11 -- macOS 10.15 ~ 11 Big Sur + +- Windows 10 / 11 +- macOS 10.15 ~ 11 (Big Sur) - Ubuntu 20.04 - Raspberry Pi 3B+ -- All test cases are located in the test directory + +## License + +This project is licensed under the [MIT License](LICENSE). + +## Contributing + +See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines. + +## Links + +- **PyPI**: https://pypi.org/project/je_load_density/ +- **Documentation**: https://loaddensity.readthedocs.io/en/latest/ +- **Source Code**: https://github.com/Intergration-Automation-Testing/LoadDensity diff --git a/README/README_zh-CN.md b/README/README_zh-CN.md new file mode 100644 index 0000000..fcd92f3 --- /dev/null +++ b/README/README_zh-CN.md @@ -0,0 +1,358 @@ +# LoadDensity + +[![Python](https://img.shields.io/pypi/pyversions/je_load_density)](https://pypi.org/project/je_load_density/) +[![PyPI](https://img.shields.io/pypi/v/je_load_density)](https://pypi.org/project/je_load_density/) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) +[![Documentation](https://readthedocs.org/projects/loaddensity/badge/?version=latest)](https://loaddensity.readthedocs.io/en/latest/) + +**LoadDensity** 是一个基于 [Locust](https://locust.io/) 构建的高性能负载与压力测试自动化框架。它对 Locust 的核心功能进行了简化封装,提供快速用户生成、通过模板与 JSON 脚本进行灵活测试配置、多格式报告生成(HTML / JSON / XML)、内置 GUI 图形界面、通过 TCP Socket 服务器进行远程执行,以及测试后工作流程的回调机制。 + +**[English](../README.md)** | **[繁體中文](README_zh-TW.md)** + +--- + +## 功能特性 + +- **简化的 Locust 封装** — 将 Locust 的 `Environment`、`Runner` 和 `User` 类抽象化为简洁的高层 API。 +- **两种用户类型** — 同时支持 `HttpUser` 和 `FastHttpUser`(基于 geventhttpclient,吞吐量更高)。 +- **快速用户生成** — 可配置生成速率,轻松扩展至数千名并发用户。 +- **JSON 驱动的测试脚本** — 将测试场景定义为 JSON 文件,无需编写 Python 代码即可执行。 +- **动作执行器** — 内置的事件驱动执行器,将动作名称映射到函数。支持批量执行与文件驱动执行。 +- **报告生成** — 导出三种格式的测试结果: + - **HTML** — 包含成功/失败记录的样式化表格 + - **JSON** — 适合程序化处理的结构化数据 + - **XML** — 标准 XML 输出,适合 CI/CD 集成 +- **请求钩子** — 自动记录每个请求(成功与失败),包含方法、URL、状态码、响应内容、头部与错误信息。 +- **回调执行器** — 将触发函数与回调函数串联,用于测试后工作流程(例如:执行测试后自动生成报告)。 +- **TCP Socket 服务器** — 基于 gevent 的远程执行服务器。通过 TCP 接收 JSON 命令以远程执行测试。 +- **项目脚手架** — 自动生成项目目录结构,包含关键字模板与执行器脚本。 +- **包管理器** — 在运行时动态加载外部 Python 包,并将其函数注册到执行器中。 +- **GUI 图形界面(可选)** — 基于 PySide6 的图形界面,支持实时日志显示,提供英文与繁体中文界面。 +- **CLI 命令行支持** — 直接从命令行执行测试、运行脚本或创建项目结构。 +- **跨平台** — 支持 Windows、macOS 和 Linux。 + +## 安装 + +### 基本安装(CLI 与库) + +```bash +pip install je_load_density +``` + +### 包含 GUI 支持 + +```bash +pip install je_load_density[gui] +``` + +这会安装 [PySide6](https://doc.qt.io/qtforpython/) 和 [qt-material](https://github.com/UN-GCPDS/qt-material) 以提供图形界面。 + +## 系统要求 + +- Python **3.10** 或更高版本 +- [Locust](https://locust.io/)(会作为依赖项自动安装) + +## 快速上手 + +### 1. 使用 Python API + +```python +from je_load_density import start_test + +# 定义用户配置与任务 +result = start_test( + user_detail_dict={"user": "fast_http_user"}, + user_count=50, + spawn_rate=10, + test_time=10, + tasks={ + "get": {"request_url": "http://httpbin.org/get"}, + "post": {"request_url": "http://httpbin.org/post"}, + } +) +``` + +**参数说明:** +| 参数 | 类型 | 默认值 | 说明 | +|---|---|---|---| +| `user_detail_dict` | `dict` | — | 用户类型配置。`{"user": "fast_http_user"}` 或 `{"user": "http_user"}` | +| `user_count` | `int` | `50` | 模拟用户总数 | +| `spawn_rate` | `int` | `10` | 每秒生成的用户数量 | +| `test_time` | `int` | `60` | 测试持续时间(秒)。设为 `None` 则无限执行 | +| `web_ui_dict` | `dict` | `None` | 启用 Locust Web UI,例如 `{"host": "127.0.0.1", "port": 8089}` | +| `tasks` | `dict` | — | HTTP 方法对应请求 URL 的映射 | + +### 2. 使用 JSON 脚本文件 + +创建 JSON 文件(`test_scenario.json`): + +```json +[ + ["LD_start_test", { + "user_detail_dict": {"user": "fast_http_user"}, + "user_count": 50, + "spawn_rate": 10, + "test_time": 5, + "tasks": { + "get": {"request_url": "http://httpbin.org/get"}, + "post": {"request_url": "http://httpbin.org/post"} + } + }] +] +``` + +从 Python 执行: + +```python +from je_load_density import execute_action, read_action_json + +execute_action(read_action_json("test_scenario.json")) +``` + +### 3. 使用 CLI 命令行 + +```bash +# 执行单个 JSON 脚本文件 +python -m je_load_density -e test_scenario.json + +# 执行目录中所有 JSON 文件 +python -m je_load_density -d ./test_scripts/ + +# 执行内联 JSON 字符串 +python -m je_load_density --execute_str '[["LD_start_test", {"user_detail_dict": {"user": "fast_http_user"}, "user_count": 10, "spawn_rate": 5, "test_time": 5, "tasks": {"get": {"request_url": "http://httpbin.org/get"}}}]]' + +# 使用模板创建新项目 +python -m je_load_density -c MyProject +``` + +### 4. 使用 GUI 图形界面 + +```python +from je_load_density.gui.main_window import LoadDensityUI +from PySide6.QtWidgets import QApplication +import sys + +app = QApplication(sys.argv) +window = LoadDensityUI() +window.show() +sys.exit(app.exec()) +``` + +## 报告生成 + +执行测试后,从记录的数据生成报告: + +```python +from je_load_density import ( + generate_html_report, + generate_json_report, + generate_xml_report, +) + +# HTML 报告 — 创建 "my_report.html" +generate_html_report("my_report") + +# JSON 报告 — 创建 "my_report_success.json" 和 "my_report_failure.json" +generate_json_report("my_report") + +# XML 报告 — 创建 "my_report_success.xml" 和 "my_report_failure.xml" +generate_xml_report("my_report") +``` + +## 高级用法 + +### 动作执行器 + +执行器将字符串动作名称映射到可调用的函数。所有 Python 内置函数也可使用。 + +```python +from je_load_density import executor, add_command_to_executor + +# 注册自定义函数 +def my_custom_action(message): + print(f"自定义动作: {message}") + +add_command_to_executor({"my_action": my_custom_action}) + +# 程序化执行动作 +executor.execute_action([ + ["my_action", ["Hello World"]], + ["print", ["测试完成"]], +]) +``` + +**内置执行器动作:** +| 动作名称 | 说明 | +|---|---| +| `LD_start_test` | 启动负载测试 | +| `LD_generate_html` | 生成 HTML 片段 | +| `LD_generate_html_report` | 生成完整 HTML 报告文件 | +| `LD_generate_json` | 生成 JSON 数据结构 | +| `LD_generate_json_report` | 生成 JSON 报告文件 | +| `LD_generate_xml` | 生成 XML 字符串 | +| `LD_generate_xml_report` | 生成 XML 报告文件 | +| `LD_execute_action` | 执行动作列表 | +| `LD_execute_files` | 从多个文件执行动作 | +| `LD_add_package_to_executor` | 动态加载包到执行器 | + +### 回调执行器 + +将触发函数与回调串联: + +```python +from je_load_density import callback_executor + +def after_test(): + print("测试完成,正在生成报告...") + +callback_executor.callback_function( + trigger_function_name="user_test", + callback_function=after_test, + user_detail_dict={"user": "fast_http_user"}, + user_count=10, + spawn_rate=5, + test_time=5, + tasks={"get": {"request_url": "http://httpbin.org/get"}}, +) +``` + +### TCP Socket 服务器(远程执行) + +启动接收 JSON 命令的 TCP 服务器: + +```python +from je_load_density import start_load_density_socket_server + +# 启动服务器(阻塞式) +start_load_density_socket_server(host="localhost", port=9940) +``` + +从客户端发送命令: + +```python +import socket, json + +sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +sock.connect(("localhost", 9940)) + +command = json.dumps([ + ["LD_start_test", { + "user_detail_dict": {"user": "fast_http_user"}, + "user_count": 10, "spawn_rate": 5, "test_time": 5, + "tasks": {"get": {"request_url": "http://httpbin.org/get"}} + }] +]) +sock.send(command.encode("utf-8")) +response = sock.recv(8192) +print(response.decode("utf-8")) +sock.close() +``` + +发送 `"quit_server"` 可优雅地关闭服务器。 + +### 项目脚手架 + +生成包含关键字模板与执行器脚本的项目: + +```python +from je_load_density import create_project_dir + +create_project_dir(project_path="./my_tests", parent_name="LoadDensity") +``` + +这会创建以下结构: +``` +my_tests/ +└── LoadDensity/ + ├── keyword/ + │ ├── keyword1.json # FastHttpUser 测试模板 + │ └── keyword2.json # HttpUser 测试模板 + └── executor/ + ├── executor_one_file.py # 执行单个关键字文件 + └── executor_folder.py # 执行 keyword/ 目录中所有文件 +``` + +### 动态包加载 + +加载外部包并将其函数注册到执行器中: + +```python +from je_load_density import executor + +# 加载包并使其函数可作为执行器动作使用 +executor.execute_action([ + ["LD_add_package_to_executor", ["my_custom_package"]] +]) +``` + +### 测试记录 + +以程序化方式访问原始测试记录: + +```python +from je_load_density import test_record_instance + +# 执行测试后 +for record in test_record_instance.test_record_list: + print(record["Method"], record["test_url"], record["status_code"]) + +for error in test_record_instance.error_record_list: + print(error["Method"], error["test_url"], error["error"]) + +# 清除记录 +test_record_instance.clear_records() +``` + +## 架构 + +``` +je_load_density/ +├── __init__.py # 公开 API 导出 +├── __main__.py # CLI 入口点 +├── gui/ # PySide6 GUI(可选依赖) +│ ├── main_window.py # 主窗口(QMainWindow) +│ ├── main_widget.py # 测试参数表单与日志面板 +│ ├── load_density_gui_thread.py # 测试后台线程 +│ ├── log_to_ui_filter.py # GUI 显示的日志拦截器 +│ └── language_wrapper/ # 国际化(英文、繁体中文) +├── wrapper/ +│ ├── create_locust_env/ # Locust Environment 与 Runner 设置 +│ ├── start_wrapper/ # 高层 start_test() 入口点 +│ ├── user_template/ # HttpUser 与 FastHttpUser 封装 +│ ├── proxy/ # 用户代理容器与配置 +│ └── event/ # 请求钩子(记录所有请求) +└── utils/ + ├── executor/ # 动作执行器(事件驱动) + ├── generate_report/ # HTML、JSON、XML 报告生成器 + ├── test_record/ # 测试记录存储 + ├── socket_server/ # 远程执行 TCP 服务器 + ├── callback/ # 回调函数执行器 + ├── project/ # 项目脚手架与模板 + ├── package_manager/ # 动态包加载 + ├── json/ # JSON 文件读写工具 + ├── xml/ # XML 结构工具 + ├── file_process/ # 目录文件列表 + ├── logging/ # Logger 实例 + └── exception/ # 自定义异常与错误标签 +``` + +## 已测试平台 + +- Windows 10 / 11 +- macOS 10.15 ~ 11(Big Sur) +- Ubuntu 20.04 +- Raspberry Pi 3B+ + +## 许可证 + +本项目采用 [MIT 许可证](../LICENSE)。 + +## 贡献指南 + +请参阅 [CONTRIBUTING.md](../CONTRIBUTING.md) 了解贡献规范。 + +## 相关链接 + +- **PyPI**:https://pypi.org/project/je_load_density/ +- **文档**:https://loaddensity.readthedocs.io/en/latest/ +- **源代码**:https://github.com/Intergration-Automation-Testing/LoadDensity diff --git a/README/README_zh-TW.md b/README/README_zh-TW.md new file mode 100644 index 0000000..0245400 --- /dev/null +++ b/README/README_zh-TW.md @@ -0,0 +1,358 @@ +# LoadDensity + +[![Python](https://img.shields.io/pypi/pyversions/je_load_density)](https://pypi.org/project/je_load_density/) +[![PyPI](https://img.shields.io/pypi/v/je_load_density)](https://pypi.org/project/je_load_density/) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) +[![Documentation](https://readthedocs.org/projects/loaddensity/badge/?version=latest)](https://loaddensity.readthedocs.io/en/latest/) + +**LoadDensity** 是一個基於 [Locust](https://locust.io/) 建構的高效能負載與壓力測試自動化框架。它對 Locust 的核心功能進行了簡化封裝,提供快速使用者生成、透過模板與 JSON 腳本進行彈性測試配置、多格式報告生成(HTML / JSON / XML)、內建 GUI 圖形介面、透過 TCP Socket 伺服器進行遠端執行,以及測試後工作流程的回呼機制。 + +**[English](../README.md)** | **[简体中文](README_zh-CN.md)** + +--- + +## 功能特色 + +- **簡化的 Locust 封裝** — 將 Locust 的 `Environment`、`Runner` 和 `User` 類別抽象化為簡潔的高階 API。 +- **兩種使用者類型** — 同時支援 `HttpUser` 和 `FastHttpUser`(基於 geventhttpclient,吞吐量更高)。 +- **快速使用者生成** — 可配置生成速率,輕鬆擴展至數千名並行使用者。 +- **JSON 驅動的測試腳本** — 將測試場景定義為 JSON 檔案,無需撰寫 Python 程式碼即可執行。 +- **動作執行器** — 內建的事件驅動執行器,將動作名稱映射到函式。支援批次執行與檔案驅動執行。 +- **報告生成** — 匯出三種格式的測試結果: + - **HTML** — 包含成功/失敗記錄的樣式化表格 + - **JSON** — 適合程式化處理的結構化資料 + - **XML** — 標準 XML 輸出,適合 CI/CD 整合 +- **請求鉤子** — 自動記錄每個請求(成功與失敗),包含方法、URL、狀態碼、回應內容、標頭與錯誤資訊。 +- **回呼執行器** — 將觸發函式與回呼函式串聯,用於測試後工作流程(例如:執行測試後自動生成報告)。 +- **TCP Socket 伺服器** — 基於 gevent 的遠端執行伺服器。透過 TCP 接收 JSON 指令以遠端執行測試。 +- **專案腳手架** — 自動生成專案目錄結構,包含關鍵字模板與執行器腳本。 +- **套件管理器** — 在執行期間動態載入外部 Python 套件,並將其函式註冊到執行器中。 +- **GUI 圖形介面(選用)** — 基於 PySide6 的圖形介面,支援即時日誌顯示,提供英文與繁體中文介面。 +- **CLI 命令列支援** — 直接從命令列執行測試、運行腳本或建立專案結構。 +- **跨平台** — 支援 Windows、macOS 和 Linux。 + +## 安裝 + +### 基本安裝(CLI 與函式庫) + +```bash +pip install je_load_density +``` + +### 包含 GUI 支援 + +```bash +pip install je_load_density[gui] +``` + +這會安裝 [PySide6](https://doc.qt.io/qtforpython/) 和 [qt-material](https://github.com/UN-GCPDS/qt-material) 以提供圖形介面。 + +## 系統需求 + +- Python **3.10** 或更高版本 +- [Locust](https://locust.io/)(會作為依賴項自動安裝) + +## 快速上手 + +### 1. 使用 Python API + +```python +from je_load_density import start_test + +# 定義使用者配置與任務 +result = start_test( + user_detail_dict={"user": "fast_http_user"}, + user_count=50, + spawn_rate=10, + test_time=10, + tasks={ + "get": {"request_url": "http://httpbin.org/get"}, + "post": {"request_url": "http://httpbin.org/post"}, + } +) +``` + +**參數說明:** +| 參數 | 類型 | 預設值 | 說明 | +|---|---|---|---| +| `user_detail_dict` | `dict` | — | 使用者類型配置。`{"user": "fast_http_user"}` 或 `{"user": "http_user"}` | +| `user_count` | `int` | `50` | 模擬使用者總數 | +| `spawn_rate` | `int` | `10` | 每秒生成的使用者數量 | +| `test_time` | `int` | `60` | 測試持續時間(秒)。設為 `None` 則無限執行 | +| `web_ui_dict` | `dict` | `None` | 啟用 Locust Web UI,例如 `{"host": "127.0.0.1", "port": 8089}` | +| `tasks` | `dict` | — | HTTP 方法對應請求 URL 的映射 | + +### 2. 使用 JSON 腳本檔案 + +建立 JSON 檔案(`test_scenario.json`): + +```json +[ + ["LD_start_test", { + "user_detail_dict": {"user": "fast_http_user"}, + "user_count": 50, + "spawn_rate": 10, + "test_time": 5, + "tasks": { + "get": {"request_url": "http://httpbin.org/get"}, + "post": {"request_url": "http://httpbin.org/post"} + } + }] +] +``` + +從 Python 執行: + +```python +from je_load_density import execute_action, read_action_json + +execute_action(read_action_json("test_scenario.json")) +``` + +### 3. 使用 CLI 命令列 + +```bash +# 執行單一 JSON 腳本檔案 +python -m je_load_density -e test_scenario.json + +# 執行目錄中所有 JSON 檔案 +python -m je_load_density -d ./test_scripts/ + +# 執行內嵌 JSON 字串 +python -m je_load_density --execute_str '[["LD_start_test", {"user_detail_dict": {"user": "fast_http_user"}, "user_count": 10, "spawn_rate": 5, "test_time": 5, "tasks": {"get": {"request_url": "http://httpbin.org/get"}}}]]' + +# 使用模板建立新專案 +python -m je_load_density -c MyProject +``` + +### 4. 使用 GUI 圖形介面 + +```python +from je_load_density.gui.main_window import LoadDensityUI +from PySide6.QtWidgets import QApplication +import sys + +app = QApplication(sys.argv) +window = LoadDensityUI() +window.show() +sys.exit(app.exec()) +``` + +## 報告生成 + +執行測試後,從記錄的資料生成報告: + +```python +from je_load_density import ( + generate_html_report, + generate_json_report, + generate_xml_report, +) + +# HTML 報告 — 建立 "my_report.html" +generate_html_report("my_report") + +# JSON 報告 — 建立 "my_report_success.json" 和 "my_report_failure.json" +generate_json_report("my_report") + +# XML 報告 — 建立 "my_report_success.xml" 和 "my_report_failure.xml" +generate_xml_report("my_report") +``` + +## 進階用法 + +### 動作執行器 + +執行器將字串動作名稱映射到可呼叫的函式。所有 Python 內建函式也可使用。 + +```python +from je_load_density import executor, add_command_to_executor + +# 註冊自訂函式 +def my_custom_action(message): + print(f"自訂動作: {message}") + +add_command_to_executor({"my_action": my_custom_action}) + +# 程式化執行動作 +executor.execute_action([ + ["my_action", ["Hello World"]], + ["print", ["測試完成"]], +]) +``` + +**內建執行器動作:** +| 動作名稱 | 說明 | +|---|---| +| `LD_start_test` | 啟動負載測試 | +| `LD_generate_html` | 生成 HTML 片段 | +| `LD_generate_html_report` | 生成完整 HTML 報告檔案 | +| `LD_generate_json` | 生成 JSON 資料結構 | +| `LD_generate_json_report` | 生成 JSON 報告檔案 | +| `LD_generate_xml` | 生成 XML 字串 | +| `LD_generate_xml_report` | 生成 XML 報告檔案 | +| `LD_execute_action` | 執行動作列表 | +| `LD_execute_files` | 從多個檔案執行動作 | +| `LD_add_package_to_executor` | 動態載入套件到執行器 | + +### 回呼執行器 + +將觸發函式與回呼串聯: + +```python +from je_load_density import callback_executor + +def after_test(): + print("測試完成,正在生成報告...") + +callback_executor.callback_function( + trigger_function_name="user_test", + callback_function=after_test, + user_detail_dict={"user": "fast_http_user"}, + user_count=10, + spawn_rate=5, + test_time=5, + tasks={"get": {"request_url": "http://httpbin.org/get"}}, +) +``` + +### TCP Socket 伺服器(遠端執行) + +啟動接收 JSON 指令的 TCP 伺服器: + +```python +from je_load_density import start_load_density_socket_server + +# 啟動伺服器(阻塞式) +start_load_density_socket_server(host="localhost", port=9940) +``` + +從客戶端發送指令: + +```python +import socket, json + +sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +sock.connect(("localhost", 9940)) + +command = json.dumps([ + ["LD_start_test", { + "user_detail_dict": {"user": "fast_http_user"}, + "user_count": 10, "spawn_rate": 5, "test_time": 5, + "tasks": {"get": {"request_url": "http://httpbin.org/get"}} + }] +]) +sock.send(command.encode("utf-8")) +response = sock.recv(8192) +print(response.decode("utf-8")) +sock.close() +``` + +發送 `"quit_server"` 可優雅地關閉伺服器。 + +### 專案腳手架 + +生成包含關鍵字模板與執行器腳本的專案: + +```python +from je_load_density import create_project_dir + +create_project_dir(project_path="./my_tests", parent_name="LoadDensity") +``` + +這會建立以下結構: +``` +my_tests/ +└── LoadDensity/ + ├── keyword/ + │ ├── keyword1.json # FastHttpUser 測試模板 + │ └── keyword2.json # HttpUser 測試模板 + └── executor/ + ├── executor_one_file.py # 執行單一關鍵字檔案 + └── executor_folder.py # 執行 keyword/ 目錄中所有檔案 +``` + +### 動態套件載入 + +載入外部套件並將其函式註冊到執行器中: + +```python +from je_load_density import executor + +# 載入套件並使其函式可作為執行器動作使用 +executor.execute_action([ + ["LD_add_package_to_executor", ["my_custom_package"]] +]) +``` + +### 測試記錄 + +以程式化方式存取原始測試記錄: + +```python +from je_load_density import test_record_instance + +# 執行測試後 +for record in test_record_instance.test_record_list: + print(record["Method"], record["test_url"], record["status_code"]) + +for error in test_record_instance.error_record_list: + print(error["Method"], error["test_url"], error["error"]) + +# 清除記錄 +test_record_instance.clear_records() +``` + +## 架構 + +``` +je_load_density/ +├── __init__.py # 公開 API 匯出 +├── __main__.py # CLI 進入點 +├── gui/ # PySide6 GUI(選用依賴) +│ ├── main_window.py # 主視窗(QMainWindow) +│ ├── main_widget.py # 測試參數表單與日誌面板 +│ ├── load_density_gui_thread.py # 測試背景執行緒 +│ ├── log_to_ui_filter.py # GUI 顯示的日誌攔截器 +│ └── language_wrapper/ # 國際化(英文、繁體中文) +├── wrapper/ +│ ├── create_locust_env/ # Locust Environment 與 Runner 設定 +│ ├── start_wrapper/ # 高階 start_test() 進入點 +│ ├── user_template/ # HttpUser 與 FastHttpUser 封裝 +│ ├── proxy/ # 使用者代理容器與配置 +│ └── event/ # 請求鉤子(記錄所有請求) +└── utils/ + ├── executor/ # 動作執行器(事件驅動) + ├── generate_report/ # HTML、JSON、XML 報告生成器 + ├── test_record/ # 測試記錄儲存 + ├── socket_server/ # 遠端執行 TCP 伺服器 + ├── callback/ # 回呼函式執行器 + ├── project/ # 專案腳手架與模板 + ├── package_manager/ # 動態套件載入 + ├── json/ # JSON 檔案讀寫工具 + ├── xml/ # XML 結構工具 + ├── file_process/ # 目錄檔案列表 + ├── logging/ # Logger 實例 + └── exception/ # 自訂例外與錯誤標籤 +``` + +## 已測試平台 + +- Windows 10 / 11 +- macOS 10.15 ~ 11(Big Sur) +- Ubuntu 20.04 +- Raspberry Pi 3B+ + +## 授權條款 + +本專案採用 [MIT 授權條款](../LICENSE)。 + +## 貢獻指南 + +請參閱 [CONTRIBUTING.md](../CONTRIBUTING.md) 了解貢獻規範。 + +## 相關連結 + +- **PyPI**:https://pypi.org/project/je_load_density/ +- **文件**:https://loaddensity.readthedocs.io/en/latest/ +- **原始碼**:https://github.com/Intergration-Automation-Testing/LoadDensity