Skip to main content

developing-funboost-testing

当需要为 funboost 框架编写测试、运行测试脚本、或验证新 broker/mixin 实现时使用。触发场景:编写测试脚本、验证消费者/发布者功能、AI Agent 运行 funboost 脚本。关键词:test funboost, 写测试, 运行测试, test_frame, regression test, 验证消费, AI testing, os._exit, timeout。

Source facts

Repository
ydf0509/funboost
Last source activity
August 4, 2026 at 14:03
Detected SKILL.md language
Chinese
Stars
892
Forks
165

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
developing-funboost-testing
description
当需要为 funboost 框架编写测试、运行测试脚本、或验证新 broker/mixin 实现时使用。触发场景:编写测试脚本、验证消费者/发布者功能、AI Agent 运行 funboost 脚本。关键词:test funboost, 写测试, 运行测试, test_frame, regression test, 验证消费, AI testing, os._exit, timeout。
# Funboost 测试规范 ## 概述 Funboost 使用独立的 Python 脚本作为测试(非 pytest)。核心挑战:`consume()` 启动后是无限循环,测试必须设定超时或 `os._exit()` 来终止。 **核心原则:** 每个测试必须有终止机制——要么外部超时,要么内部 `os._exit()`。 ## 适用场景 - 为新 broker 实现编写测试 - 验证 mixin/扩展行为 - 运行已有测试脚本 - 修复 bug 后编写回归测试 - AI Agent 需要验证 funboost 代码 ## 测试目录规范 | 类型 | 目录 | 场景 | |------|------|------| | AI 编写的 demo/示例 | `tests/ai_codes/ai_demos/{子文件夹}/` | AI 写的示例代码(必须新建子文件夹) | | AI 回归测试 | `tests/ai_codes/regression_testing/` | AI 测试框架修改 | | 人工测试 | `test_frame/` | 框架作者的手动测试 | **注意:** AI 写 demo 必须在 `ai_demos/` 下新建合理的子文件夹,不要直接在 `ai_demos/` 根下放脚本。 ## 测试脚本模板 ```python """测试:[测试内容描述]""" import os import time # PYTHONPATH 必须在运行脚本前通过命令行设置,不要在脚本内设置 # PowerShell: $env:PYTHONPATH="D:\codes\funboost" # CMD: set PYTHONPATH=D:\codes\funboost os.environ["LOG_PATH"] = r"D:\pythonlogs\ai_console_outs" os.environ["PRINT_WRTIE_FILE_NAME"] = "test_feature_xyz_run1" os.environ["SYS_STD_FILE_NAME"] = "test_feature_xyz_std1" from funboost import boost, BoosterParams, BrokerEnum @boost(BoosterParams( queue_name="test_feature_xyz", broker_kind=BrokerEnum.SQLITE_QUEUE, # 零配置,适合测试 concurrent_num=5, qps=10, )) def test_task(x: int): print(f"处理 {x},结果 = {x * 2}") return x * 2 if __name__ == "__main__": # 发布测试消息 for i in range(10): test_task.push(i) # 启动消费 test_task.consume() # 等待足够时间后自动退出 time.sleep(15) os._exit(66) ``` ## 两种运行方式(AI Agent) ### 方式一:内部 os._exit + 日志文件(推荐) ```python import os import time os.environ["LOG_PATH"] = r"D:\pythonlogs\ai_console_outs" os.environ["PRINT_WRTIE_FILE_NAME"] = "test_xyz_run1" os.environ["SYS_STD_FILE_NAME"] = "test_xyz_std1" # ... 测试代码 ... time.sleep(15) os._exit(66) ``` 运行后读取 `D:\pythonlogs\ai_console_outs\` 下生成的日志文件查看输出(nb_log 会自动添加日期前缀,如 `2026-06-29.0001.test_xyz_run1.print`,需模糊搜索匹配)。 **重要:** 每次运行使用不同的文件名后缀(时间戳或序号),避免读到上次的老日志。 ### 方式二:subprocess.run 外部超时 ```python import subprocess result = subprocess.run( ['python', 'tests/ai_codes/my_test.py'], cwd=r'D:\codes\funboost', timeout=30, env={**os.environ, 'PYTHONPATH': r'D:\codes\funboost'} ) ``` 脚本无需 `os._exit` — subprocess 超时后自动终止子进程。 ⚠️ **不要使用** `cmd /c "timeout /t N & python xxx.py"`:`timeout` 命令是先空等 N 秒再启动 python,不会限制 python 运行时长。 **超时/休眠时间估算(一般要大于 10 秒):** - 框架启动时间:5-10 秒 - 默认值:`concurrent_num=50`,`qps=None`(不限制) - 不设 qps 时:`实际吞吐量 ≈ concurrent_num / 函数耗时` - 设 qps 时:`实际吞吐量 ≈ min(qps, concurrent_num / 函数耗时)` - `合理时间 ≈ 框架启动时间(5-10秒) + (消息个数 / 实际吞吐量) + 缓冲时间(2-5秒)` - **最大不超过 50 秒** ## 测试验证清单 Broker 测试: - [ ] 消息发布成功(无发布错误) - [ ] 消费者正常启动(无错误) - [ ] 消息被消费(print/日志显示处理结果) - [ ] 正确数量的消息被处理 - [ ] ACK 正常工作(消息不会重复出现) - [ ] 重试功能正常(模拟失败时) Mixin 测试: - [ ] Mixin 初始化成功(`custom_init` 执行) - [ ] 钩子方法触发(前置/后置) - [ ] super() 链正常工作(MRO 不断裂) - [ ] `user_options` 中的配置被正确读取 - [ ] 多个 mixin 组合正常 ## PYTHONPATH 必须设置 **运行任何 funboost 脚本前必须设置 PYTHONPATH = 项目根目录:** ```powershell # PowerShell $env:PYTHONPATH="D:\codes\funboost" # CMD set PYTHONPATH=D:\codes\funboost ``` **为什么必须设置:** 1. 解决 `import funboost` 的路径问题 2. **funboost 启动时通过 `importlib.import_module('funboost_config')` 从 `sys.path` 中加载配置文件。** 设置 PYTHONPATH 让项目根目录进入 `sys.path`,框架就能找到 `funboost_config.py` 并读取 Redis/RabbitMQ 等连接配置。不设则用框架默认值(localhost)。首次运行找不到时会自动生成配置模板。 ## 示例:测试新 Broker ```python """测试:TXT 文件 broker""" import os import time # PYTHONPATH 须在命令行设置,脚本内设置无效 os.environ["LOG_PATH"] = r"D:\pythonlogs\ai_console_outs" os.environ["PRINT_WRTIE_FILE_NAME"] = "test_txt_broker_run1" os.environ["SYS_STD_FILE_NAME"] = "test_txt_broker_std1" from funboost import boost, BoosterParams, BrokerEnum @boost(BoosterParams( queue_name="test_txt_broker", broker_kind=BrokerEnum.TXT_FILE, concurrent_num=3, qps=5, )) def txt_task(name: str, value: int): result = f"{name}={value * 10}" print(f"[OK] {result}") return result if __name__ == "__main__": # 阶段1:发布 for i in range(5): txt_task.push(f"item_{i}", value=i) print(f"已发布 5 条消息") # 阶段2:消费 txt_task.consume() # 阶段3:等待并验证 time.sleep(15) print("测试完成") os._exit(66) ``` ## 示例:测试 Mixin ```python """测试:自定义监控 mixin""" import os import time # PYTHONPATH 须在命令行设置,脚本内设置无效 os.environ["LOG_PATH"] = r"D:\pythonlogs\ai_console_outs" os.environ["PRINT_WRTIE_FILE_NAME"] = "test_mixin_run1" os.environ["SYS_STD_FILE_NAME"] = "test_mixin_std1" from funboost import boost, BoosterParams, BrokerEnum from funboost.consumers.base_consumer import AbstractConsumer class CounterMixin(AbstractConsumer): def custom_init(self): super().custom_init() self._success_count = 0 self._fail_count = 0 def _both_sync_and_aio_frame_custom_record_process_info_func(self, status, kw): super()._both_sync_and_aio_frame_custom_record_process_info_func(status, kw) if status.success: self._success_count += 1 else: self._fail_count += 1 print(f"[COUNTER] success={self._success_count}, fail={self._fail_count}") @boost(BoosterParams( queue_name="test_counter_mixin", broker_kind=BrokerEnum.SQLITE_QUEUE, consumer_override_cls=CounterMixin, concurrent_num=2, qps=5, )) def counted_task(x: int): if x % 3 == 0: raise ValueError(f"模拟失败 {x}") return x * 2 if __name__ == "__main__": for i in range(10): counted_task.push(i) counted_task.consume() time.sleep(20) os._exit(66) ``` ## 判断测试是否通过 **退出码 66 不代表测试通过!** 退出码只说明脚本跑完了 sleep 没有提前崩溃,但无法证明任务是否被正确消费执行。AI 必须**读取控制台输出或日志文件**,确认: - 有预期的 print 输出(如函数执行结果) - 无 Error/Exception 堆栈 - 消费次数与 push 次数一致 ## 相关 Skill - `using-funboost-basics` — 基础使用入门 - `funboost-troubleshooting` — 排错与 FAQ
View on GitHub