28 KiB
TDD.md:小红书 / 抖音热榜评论抓取 + AI 分析报告工具
1. 文档信息
- 文档阶段:TDD(Test-Driven Development,测试驱动开发计划)
- 需求依据:
docs/PRD.md、docs/FeatureSummary.md - 技术依据:
docs/DevelopmentPlan.md - UI 依据:
docs/UIDesign.md - 当前目标:定义 MVP 开发前必须先写的测试范围、测试顺序、Mock 策略、验收命令与端到端测试清单
- 测试原则:先写失败测试,再写最小实现;任何业务代码变更前必须先有对应测试
2. TDD 总原则
2.1 红绿重构流程
每个功能点按以下顺序执行:
- Red:先写一个最小失败测试。
- Verify Red:运行测试,确认失败原因是目标功能缺失,而不是测试代码错误。
- Green:写最小实现让测试通过。
- Verify Green:运行该测试和相关测试,确认通过。
- Refactor:只在测试通过后清理代码。
禁止事项:
- 禁止先实现后补测试。
- 禁止为了让测试通过而删除关键断言。
- 禁止用真实 TikHub / AI API 作为单元测试依赖。
- 禁止在模板中硬编码中文状态文案,状态展示必须通过映射或 Macro 测试覆盖。
2.2 测试分层
| 层级 | 工具 | 目标 |
|---|---|---|
| 单元测试 | pytest | 字段映射、配置校验、分页、统计、导出、AI schema |
| 服务集成测试 | pytest + TestClient + mock httpx | 任务创建、后台流程、平台链路、报告生成 |
| 模板测试 | pytest + Jinja2 渲染 | 状态 Badge、空状态、导出按钮、标签渲染 |
| 端到端测试 | Playwright | 首页创建任务、查看报告、导出按钮、手动刷新 |
| Docker 验收 | docker compose + curl | 启动、/health、页面可访问 |
2.3 测试目录建议
tests/
conftest.py
fixtures/
xhs_hot_list.json
xhs_search_notes.json
xhs_comments_page_1.json
xhs_comments_page_2_empty.json
douyin_hot_list.json
douyin_search_videos.json
douyin_comments_page_1.json
ai_comments_success.json
ai_comments_invalid_json.txt
unit/
test_config.py
test_models.py
test_xiaohongshu_mapping.py
test_douyin_mapping.py
test_comment_pagination.py
test_ai_schema.py
test_report_stats.py
test_export.py
test_template_filters.py
integration/
test_task_creation.py
test_task_flow_xiaohongshu.py
test_task_flow_douyin.py
test_failure_tolerance.py
test_routes.py
e2e/
test_mvp_flow.py
3. 测试环境与命令
3.1 推荐依赖
pytest
pytest-cov
pytest-mock
respx
httpx
beautifulsoup4
playwright
说明:
respx用于 mockhttpx.Client外部 HTTP 调用。beautifulsoup4用于断言 HTML 片段内容。- Playwright 仅覆盖关键页面流程,不替代单元测试。
工具使用原则
respx:用于 mockhttpx.Client发出的外部 HTTP 请求(TikHub API、AI API)。pytest-mock:用于 mock Python 函数、类方法、time.sleep调用和时间函数。- 两者不混用于同一外部请求的 mock。即:若某个测试需要拦截一个 HTTP 请求,必须使用
respx,不得用pytest-mock.patch("httpx.Client.get")替代。
3.2 测试数据库隔离与环境变量
测试数据库隔离策略
所有单元测试和集成测试统一使用内存数据库 sqlite:///:memory:,不依赖物理 test.db 文件,避免测试用例间状态污染(Flaky Tests)。
在 tests/conftest.py 中定义函数级别(scope="function")的数据库 fixture:
@pytest.fixture(scope="function")
def db_session():
engine = create_engine(
"sqlite:///:memory:",
connect_args={"check_same_thread": False},
)
Base.metadata.create_all(engine)
session = Session(engine)
yield session
session.close()
Base.metadata.drop_all(engine)
每个测试用例运行前自动创建全部表结构,运行后自动销毁,确保测试在绝对隔离的环境中执行。
禁止在集成测试中复用同一 engine 实例跨用例写入数据而不做 teardown。
环境变量
APP_ENV=test
DATABASE_URL=sqlite:///:memory:
TIKHUB_API_KEY=test-token
TIKHUB_BASE_URL=https://api.tikhub.test
AI_PROVIDER=openai-compatible
AI_BASE_URL=https://ai.test
AI_API_KEY=test-ai-key
AI_MODEL=test-model
AI_BATCH_SIZE=20
AI_CONCURRENCY=2
AI_MAX_RETRIES=3
AI_TIMEOUT_SECONDS=30
3.3 常用命令
pytest tests/unit -q
pytest tests/integration -q
pytest tests/unit tests/integration \
--cov=app \
--cov-branch \
--cov-report=term-missing
pytest tests/e2e -q
docker compose up --build
curl -f http://localhost:8000/health
覆盖率豁免配置
在 pyproject.toml 的 [tool.coverage.report] 节中添加以下配置,将模板层和静态资源排除在覆盖率统计之外:
[tool.coverage.report]
omit = [
"app/templates/*",
"app/static/*",
"tests/*",
]
全局覆盖率目标维持 80%,app/services/ 和 app/platforms/ 下的核心域覆盖率不低于 90%(通过 CI 脚本或 code review 人工把关,不在 pytest 命令层面强制差异化)。
4. Mock 数据规范
4.1 小红书 Mock 样例
xhs_hot_list.json 必须覆盖:
data.data.items[]是真实热榜条目;- 外层
data.data.title不应被误用为热点标题; - 热点字段包含
id、title、score。
xhs_search_notes.json 必须覆盖:
- 至少 2 条
comments_count > 0的笔记; - 至少 1 条
comments_count = 0的笔记; - 字段包含
note.id、note.title、note.desc、note.comments_count。
xhs_comments_page_1.json 必须覆盖:
- 评论 ID 同时存在
comment_id和id时,优先comment_id; - 评论正文可来自
content或text; - 包含
like_count、create_time。
4.2 抖音 Mock 样例
douyin_hot_list.json 必须覆盖:
- 热点字段包含
query_id、title、rank、hot_score。
douyin_search_videos.json 必须覆盖:
- 视频字段包含
aweme_info.aweme_id、aweme_info.desc、aweme_info.author、aweme_info.statistics。
douyin_comments_page_1.json 必须覆盖:
- 评论 ID 同时存在
comment_id和cid时,优先comment_id; - 评论正文来自
text; - 包含
digg_count、create_time。
4.3 AI Mock 样例
成功响应:
[
{
"comment_id": "c1",
"sentiment": "positive",
"labels": ["价格实惠", "质量好"],
"reason": "评论表达了明确认可"
}
]
失败响应至少覆盖:
- 非 JSON 文本;
- JSON Object 而不是 JSON Array;
- 缺失
comment_id; comment_id与输入不匹配;sentiment不在枚举值内;labels超过 3 个;- 混入 Markdown 或自然语言前缀。
4.4 HTTP 429 Mock Fixture
在 tests/conftest.py 中提供可复用的 429 响应 fixture:
@pytest.fixture
def mock_429_response():
return httpx.Response(
status_code=429,
headers={"Retry-After": "1"},
json={"message": "Too Many Requests"},
)
§8.2(分页限流重试)和 §13.3(任务级限流容错)均使用此 fixture,不各自重复定义。
4.5 评论字段缺失 Mock 数据
在 tests/fixtures/ 中新增以下两个文件:
xhs_comments_missing_fields.json:小红书评论列表,其中like_count和create_time字段均不存在(非 null,而是 key 完全缺失)。douyin_comments_missing_fields.json:抖音评论列表,其中digg_count和create_time字段均不存在。
这两个 fixture 用于 §7.2 和 §7.5 的字段缺失测试用例。
4.6 AI 全情绪值 Mock 数据
在 tests/fixtures/ai_comments_success.json 中确保包含四种 sentiment 值各至少一条评论,用于统计逻辑(analysis_success_rate、情绪分布)的测试:
[
{"comment_id": "c001", "sentiment": "positive", "labels": ["品质好"], "reason": ""},
{"comment_id": "c002", "sentiment": "negative", "labels": ["物流慢"], "reason": ""},
{"comment_id": "c003", "sentiment": "neutral", "labels": [], "reason": ""},
{"comment_id": "c004", "sentiment": "unknown", "labels": [], "reason": ""}
]
若 ai_comments_success.json 已有其他用途,新增 ai_comments_all_sentiments.json 作为统计测试专用 fixture。
5. 配置与数据模型测试
5.1 配置校验
测试文件:tests/unit/test_config.py
用例:
- 默认配置读取成功。
hot_limit范围为 1–10。item_limit_per_hot范围为 1–10。comment_limit_per_item范围为 10–100。AI_CONCURRENCY默认值为 2。AI_MAX_RETRIES默认值为 3。
5.2 SQLite 初始化
测试文件:tests/unit/test_models.py
用例:
- 应用启动时调用
Base.metadata.create_all(engine)可创建所有表。 - SQLite engine 配置包含
check_same_thread=False和timeout=10。 - 连接建立后启用
PRAGMA journal_mode=WAL。
5.3 任务状态模型
测试文件:tests/unit/test_models.py
用例:
Task.status仅允许running、success、failed。- 不允许写入
partial_success或partial_failed。 analysis_status允许normal、insufficient。- 任务
status=success时,analysis_success_rate < 0.8只更新analysis_status='insufficient',不改变status='success'。 - 任务
status=failed时,analysis_success_rate字段写入实际计算值,status保持'failed'不变,不因 AI 成功率判断被覆盖为其他值。
6. 任务创建与恢复测试
6.1 创建任务 API
测试文件:tests/integration/test_task_creation.py
用例:
POST /api/tasks使用合法参数创建任务,返回task_id和status=running。- 创建任务保存平台和配置字段:
hot_limititem_limit_per_hotcomment_limit_per_item
platform非法时返回 422。- 配置值越界时返回 422。
- 同一平台可重复创建任务,任务彼此独立。
6.2 ThreadPoolExecutor 单任务约束
测试文件:tests/unit/test_task_executor.py
用例:
- 任务执行器初始化为
ThreadPoolExecutor(max_workers=1)。 - 同时提交两个任务时,第二个任务必须等待第一个任务完成后才开始。
httpx 使用方式断言
测试文件:tests/unit/test_task_executor.py
用例:
- 后台任务执行函数不是协程:使用
inspect.iscoroutinefunction(run_task)断言返回False,确保其在ThreadPoolExecutor中以同步方式运行。 - 小红书平台客户端实例为
httpx.Client同步类型:assert isinstance(client._http_client, httpx.Client)。 - 抖音平台客户端实例为
httpx.Client同步类型,同上。 - AI 服务客户端发出的请求使用
httpx.Client,不存在httpx.AsyncClient的实例化调用。
6.3 僵尸任务恢复
测试文件:tests/integration/test_task_recovery.py
用例:
- 数据库存在
status=running的任务。 - 应用 lifespan 启动恢复逻辑执行后,该任务变为
status=failed。 error_stage=system。error_type=unexpected_restart。error_message=系统重启,任务被中断。
7. 平台抓取测试
7.1 小红书字段映射
测试文件:tests/unit/test_xiaohongshu_mapping.py
用例:
- 热点标题从
data.data.items[].title读取。 - 不使用外层
data.data.title作为热点标题。 - 热点
id映射到source_hot_id。 score映射到heat_value。- 笔记
note.id映射到source_item_id。 - 优先选择
comments_count > 0的笔记。 - 有评论笔记不足目标数时,补充
comments_count = 0的笔记。 - 平台返回笔记总数不足目标数时,不标记任务失败。
笔记搜索分页限制
测试文件:tests/unit/test_xiaohongshu_mapping.py
用例:
- 搜索笔记接口只请求第一页(
page=1),不发起后续翻页请求。 - 首页返回
comments_count > 0的笔记已满足目标数量时,不发起第二次搜索请求。 - 首页返回结果不足目标数量时,执行降级策略(补充
comments_count = 0的笔记),而非翻页搜索。
7.2 小红书评论字段映射
测试文件:tests/unit/test_xiaohongshu_mapping.py
用例:
source_comment_id优先取data.get("comment_id")。comment_id不存在时回退到data.get("id")。content不存在时回退到text。like_count和create_time正确映射。- 原始评论 JSON 保存到
raw_data。 like_count字段缺失时,入库值为null,不抛出异常,不阻断评论抓取流程。comment_time(create_time/create_time_str)字段缺失时,入库值为null,不抛出异常,不阻断评论抓取流程。
7.4 抖音字段映射
测试文件:tests/unit/test_douyin_mapping.py
用例:
query_id映射到source_hot_id。title映射到热点标题。rank映射到热点排名。hot_score映射到heat_value。aweme_info.aweme_id映射到source_item_id。aweme_info.desc映射到标题或摘要。
7.5 抖音评论字段映射
测试文件:tests/unit/test_douyin_mapping.py
用例:
source_comment_id优先取data.get("comment_id")。comment_id不存在时回退到data.get("cid")。text映射到content。digg_count映射到like_count。create_time映射到comment_time。digg_count字段缺失时,入库值为null,不抛出异常,不阻断评论抓取流程。comment_time(create_time/create_time_str)字段缺失时,入库值为null,不抛出异常,不阻断评论抓取流程。
8. 评论分页、限流与去重测试
8.1 评论分页终止条件
测试文件:tests/unit/test_comment_pagination.py
用例:
- 抓取评论数达到
comment_limit_per_item后停止。 - API 返回空评论列表后停止。
- 达到最大翻页轮次 5 后停止。
- API 未返回下一页游标时停止。
8.2 分页请求间隔
测试文件:tests/unit/test_comment_pagination.py
用例:
- 每次评论分页请求之间调用
time.sleep,间隔值在 1.0 到 2.0 秒范围内(含边界值)。断言方式:assert 1.0 <= mock_sleep.call_args[0][0] <= 2.0。 - 收到 HTTP 429 后使用指数退避 1s → 2s → 4s。
- 429 退避完成后恢复基础分页间隔。
备注:若后续将间隔提取为配置项 CRAWL_PAGE_INTERVAL_SECONDS,改为读取配置值后断言,不硬编码数值。
8.3 评论去重
测试文件:tests/unit/test_comment_pagination.py
用例:
- 同一任务、同一内容条目、同一评论 ID 不重复入库。
- 重复抓取时更新已有记录或跳过重复记录。
- 不同任务下相同评论 ID 可分别保存。
- 同一内容出现在不同热点下时,按
task_id + hotspot_id + source_item_id保留重复内容条目。
9. AI 分析测试
9.1 Prompt 输入构造
测试文件:tests/unit/test_ai_schema.py
用例:
- 输入给 LLM 的数据为 JSON Array。
- 每条输入包含
comment_id和content。 - 单条评论内容超过 150 字符时截断为前 150 字符。
- Prompt 明确要求原样回填
comment_id。
9.2 AI 输出 Schema 校验
测试文件:tests/unit/test_ai_schema.py
用例:
- 严格 JSON Array 响应校验通过。
- 非 JSON 文本校验失败。
- JSON Object 校验失败。
- 缺失
comment_id校验失败。 comment_id与输入不匹配时该条评论失败。sentiment不在枚举值内校验失败。labels超过 3 个时校验失败。- 空标签数组允许通过,
sentiment可为unknown。
9.3 AI 重试与降级拆分
测试文件:tests/unit/test_ai_schema.py
用例:
- 单批 AI 请求失败后最多重试
AI_MAX_RETRIES=3次。 - 同一批次连续 3 次整批解析失败(含重试)后,该批次所有评论的
ai_analysis_status标记为'failed',任务继续处理下一批次,不抛出异常。 - 当前批次失败不阻塞下一批次。
9.4 AI 并发粒度
测试文件:tests/unit/test_ai_schema.py
用例:
- 同一任务内最多 2 个 AI 批量请求同时运行。
- 并发粒度为跨内容条目。
- 同一内容条目的多批评论串行处理。
9.5 AI 成功率与任务质量状态
测试文件:tests/unit/test_ai_schema.py
用例:
- 成功评论数 / 总评论数 >= 80% 时,
analysis_status=normal。 - 成功评论数 / 总评论数 < 80% 时,
analysis_status=insufficient。 analysis_status=insufficient不改变任务status。- 没有任何内容条目成功时,任务仍为
failed。
10. 报告生成测试
10.1 情绪与标签统计
测试文件:tests/unit/test_report_stats.py
用例:
- 正向、负向、中性、未知评论数量正确。
- 情绪占比保留合理精度。
- 标签按字面值聚合。
- Top 5 标签按数量降序。
- 空标签不参与标签统计。
10.2 典型评论选取
测试文件:tests/unit/test_report_stats.py
用例:
- 每种情绪选取 1–2 条典型评论。
- 有点赞数字段时按点赞数降序。
- 点赞数缺失时按抓取顺序。
- 典型评论文本传给总结 AI 前截断为 150 字符。
10.3 内容条目级报告
测试文件:tests/unit/test_report_stats.py
用例:
- 内容条目级报告包含样本评论数量。
- 报告统计与评论结构化结果一致。
report_type=item时hotspot_id和content_item_id均不为空。- 生成
markdown_content并保存。 - 总结 AI 失败时,summary 使用默认文本:
总结生成失败,请查看详细数据。 report_type='item'时,创建的报告记录hotspot_id不为空,content_item_id不为空;任一字段为空时report_service抛出明确异常。report_type='hotspot'时,创建的报告记录hotspot_id不为空,content_item_id为空;若content_item_id非空则抛出明确异常。
10.4 热点级报告
测试文件:tests/unit/test_report_stats.py
用例:
- 热点级报告聚合热点下所有内容条目评论。
- 内容条目数量、样本数量、情绪数量与评论明细一致。
report_type=hotspot时hotspot_id不为空,content_item_id为空。- 生成
markdown_content并保存。 - 总结 AI 失败不阻断报告创建。
report_type='item'时,创建的报告记录hotspot_id不为空,content_item_id不为空;任一字段为空时report_service抛出明确异常。report_type='hotspot'时,创建的报告记录hotspot_id不为空,content_item_id为空;若content_item_id非空则抛出明确异常。
10.5 报告总结 AI 请求
测试文件:tests/unit/test_report_stats.py
用例:
- 内容条目级总结 AI 请求的输入包含统计数据摘要(正向 / 中性 / 负向各占比)和典型评论文本(每类至少 1 条)。
- 热点级总结 AI 请求的输入包含聚合情绪分布统计、Top 5 标签及出现次数,以及典型评论文本。
- 传入总结 AI 的单条评论文本截断为 150 字符,超出部分丢弃,不引发错误。
- 总结 AI 请求不使用 JSON Schema 约束(
response_format不为json_object或json_schema),输出期望为纯文本字符串。 - 总结 AI 请求超时时,报告中
summary字段写入默认文本总结生成失败,请查看上方统计数据,报告记录正常创建,不抛出异常,不阻断后续报告生成流程。 - 总结 AI 返回内容超过字数限制时执行截断:内容条目级总结超过 200 字时截断,热点级超过 300 字时截断。
11. 导出测试
11.1 CSV 导出
测试文件:tests/unit/test_export.py
用例:
- CSV 使用
UTF-8-SIG编码。 - CSV 包含平台、任务 ID、热点、内容条目、评论 ID、评论内容、情绪、标签、点赞数、评论时间。
labels入库为 JSON Array 字符串,导出时转换为中文逗号拼接。- 评论内容以
=、+、-、@开头时添加单引号,防 CSV 公式注入。 - 导出内容与页面展示数据源一致。
11.2 文件名安全处理
测试文件:tests/unit/test_export.py
用例:
- 文件名格式为
{platform}_{task_id}_{hotspot_keyword}.csv。 hotspot_keyword超过 20 字符时截断。/、\、:、*、?、"、<、>、|替换为_。- 连续多个
_合并为单个_。
11.3 Markdown 导出
测试文件:tests/unit/test_export.py
用例:
- 热点级 Markdown 读取
reports.markdown_content。 - 内容条目级 Markdown 读取
reports.markdown_content。 - 导出 Markdown 与页面报告使用同一份数据。
- 报告不存在时返回 404 或禁用按钮对应状态。
11.4 导出路由
测试文件:tests/integration/test_routes.py
用例:
GET /api/export/items/{item_id}/comments.csv返回 CSV 文件流。GET /api/export/hotspots/{hotspot_id}/comments.csv返回热点下全部评论 CSV。GET /api/export/hotspots/{hotspot_id}.md返回热点 Markdown。GET /api/export/items/{item_id}.md返回内容条目 Markdown。
12. UI 与模板测试
12.1 状态展示
测试文件:tests/unit/test_template_filters.py
用例:
running渲染为运行中 Badge。success渲染为已完成 Badge。failed渲染为失败 Badge。- 任务状态不测试
partial_success或partial_failed。 analysis_status=insufficient渲染 AI 分析不足提示。
12.2 表单与校验
测试文件:tests/integration/test_routes.py
用例:
- 首页包含平台选择控件。
- 首页包含
hot_limit、item_limit_per_hot、comment_limit_per_item输入。 - 首页展示默认预估规模 1250。
- 422 错误可渲染到表单错误区域。
12.3 标签渲染
测试文件:tests/unit/test_template_filters.py
用例:
from_jsonfilter 可解析 labels JSON Array 字符串。- 非法 JSON 返回空数组,不导致模板报错。
- 标签逐个渲染为 Badge。
- 空标签显示
-。
12.4 空状态
测试文件:tests/integration/test_routes.py
用例:
- 无任务时首页展示空状态。
- 任务运行中且热点为空时展示抓取中 Spinner。
- 评论为空时内容详情页展示暂无评论数据。
- 报告尚未生成时展示报告生成中。
12.5 导出按钮状态
测试文件:tests/integration/test_routes.py
用例:
task.status=running时导出按钮 disabled。task.status=failed且无成功内容条目时导出按钮 disabled。item.status=failed时内容条目 CSV 导出按钮 disabled。- 有报告和评论数据时导出按钮可点击。
13. 服务集成测试
13.1 小红书端到端服务流
测试文件:tests/integration/test_task_flow_xiaohongshu.py
流程:
- Mock 小红书热榜接口。
- Mock 小红书搜索笔记接口。
- Mock 小红书评论分页接口。
- Mock AI 评论分析接口。
- 创建小红书任务。
- 执行任务服务。
- 断言任务
status=success。 - 断言热点、内容条目、评论、报告均入库。
- 断言 raw_data 已保存。
13.2 抖音端到端服务流
测试文件:tests/integration/test_task_flow_douyin.py
流程:
- Mock 抖音热点接口。
- Mock 抖音视频搜索接口。
- Mock 抖音评论分页接口。
- Mock AI 评论分析接口。
- 创建抖音任务。
- 执行任务服务。
- 断言任务
status=success。 - 断言热点、内容条目、评论、报告均入库。
13.3 单个内容条目失败但任务继续
测试文件:tests/integration/test_failure_tolerance.py
用例:
- 一个内容条目评论接口返回 429 后超过最大重试。
- 该内容条目标记
failed。 - 后续内容条目继续处理。
- 至少一个内容条目成功时,任务最终
status=success。 - 任务展示失败内容条目数和错误摘要。
13.4 全局失败
测试文件:tests/integration/test_failure_tolerance.py
用例:
- 热点接口失败时任务
status=failed。 - 所有内容条目均失败时任务
status=failed。 - 失败任务包含
error_stage、error_type、error_message。
13.5 跨热点重复内容条目场景
测试文件:tests/integration/test_failure_tolerance.py
用例:
- 同一
source_item_id同时出现在两个不同热点的搜索结果中时,系统以不同hotspot_id分别创建两条content_items记录,断言数据库中存在 2 条该source_item_id的记录,各自归属于对应的hotspot_id。 - 同一任务内,同一热点下相同
source_item_id重复出现时,数据库中该热点下该source_item_id只保留 1 条记录(去重),不抛出异常,不阻断任务。
14. Playwright E2E 测试(P2,时间允许时实现)
当前 MVP 不实现本节内容。
原因:在 4 天单人开发周期内,Playwright 环境配置及 AI 长耗时任务的异步等待调优成本较高,性价比低于手工验收。
Day 4 端到端验收完全依赖 §15.2 手工验收清单,后者覆盖了所有 P0 用户流程。
后续版本实现 Playwright 时,须补充以下前提条件:
- e2e 测试启动独立测试服务器实例(
APP_ENV=test)。 - 外部 API 调用由
respx在进程内拦截,不发起真实网络请求。 - 测试前后清理 SQLite 测试数据库(或使用内存数据库)。
- 明确运行环境:若使用本地 uvicorn 服务,在 Docker 验收前执行;若在 Docker 环境中运行,须先完成 §15 Docker 验收。
15. Docker 与手工验收测试
15.1 Docker 启动
测试命令:
docker compose up --build
curl -f http://localhost:8000/health
验收:
- Docker Compose 可启动。
/health返回 200。- 首页可访问。
- SQLite 文件生成在
./data/app.db。
15.2 MVP 手工验收清单
- 创建小红书默认任务。
- 任务列表展示创建时间、状态、AI 分析状态、成功 X / 共 Y 条内容条目。
- 刷新任务状态。
- 查看热点列表。
- 查看热点级报告。
- 查看内容条目详情。
- 查看评论明细。
- 导出 CSV 评论明细。
- 导出热点 Markdown 报告。
- 导出内容条目 Markdown 报告。
- 创建抖音默认任务并重复 2–10。
- 输入非法配置,确认前端和后端均拒绝。
16. 覆盖率与发布门槛
16.1 最低覆盖要求
- 单元测试覆盖率建议不低于 80%。
platforms/、services/ai_service.py、services/report_service.py、services/export_service.py必须有关键行为测试。- UI 模板不强制覆盖率数字,但关键状态和导出按钮必须有模板测试或 e2e 测试。
16.2 合并前必须通过
pytest tests/unit -q
pytest tests/integration -q
pytest tests/unit tests/integration \
--cov=app \
--cov-branch \
--cov-report=term-missing \
--cov-fail-under=80
覆盖率统计豁免 templates/ 和 static/ 目录,详见 pyproject.toml 配置。
branch coverage 对任务状态机和 AI 成功率阈值判断有特殊检测价值。
如修改部署相关文件:
docker compose up --build
curl -f http://localhost:8000/health
17. 开发顺序建议
Day 1 测试优先级
- 配置校验测试。
- 数据模型测试。
- 任务创建 API 测试。
- 僵尸任务恢复测试。
- 首页模板基础渲染测试。
Day 2 测试优先级
- 小红书字段映射测试。
- 小红书评论分页测试。
- 小红书降级选择测试。
- 抖音字段映射测试。
- 429 退避与单条失败继续测试。
Day 3 测试优先级
- AI 输入构造测试。
- AI 输出 schema 测试。
- AI 重试测试。
analysis_success_rate测试。- 报告统计与 Markdown 生成测试。
(依赖 Day 3 第 1-4 条 AI 分析测试完成后方可验证完整集成路径)
Day 4 测试优先级
- CSV / Markdown 导出测试。
- 模板状态和空状态测试。
Playwright E2E 测试(已降级为 P2,见 §14)——Day 4 端到端验收直接执行 §15.2 手工验收清单。- Docker 启动验收。
18. 变更日志
| 日期 | 版本 | 变更内容 |
|---|---|---|
| 2026-07-01 | v1.0 | 基于 PRD、FeatureSummary、DevelopmentPlan、UIDesign 生成 TDD 测试驱动开发计划,覆盖配置、数据模型、任务、平台抓取、评论分页、AI 分析、报告、导出、UI 模板、集成流程、Playwright 与 Docker 验收。 |