测试策略和详细说明¶
本项目遵循 Rust 官方测试最佳实践,分为单元测试和集成测试。
目录¶
测试组织规范¶
单元测试 (Unit Tests)¶
- 位置: 保留在
src/目录中,与被测试代码在同一文件 - 标记: 使用
#[cfg(test)]模块 - 权限: 可以测试私有函数和内部实现
- 目的: 快速、隔离地测试单个功能单元
示例结构:
// src/common/auth.rs
pub struct CookieAuth { /* ... */ }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cookie_auth_parse() {
// 单元测试代码
}
}
集成测试 (Integration Tests)¶
- 位置: 项目根目录的
tests/目录 - 编译: 每个文件被编译为独立的 crate
- 权限: 只能访问公共 API
- 目的: 测试多个模块的协作和真实使用场景
目录结构:
项目根目录/
├── src/
│ ├── lib.rs # 可保留简单的烟雾测试
│ ├── common/
│ │ └── auth.rs # ✅ 保留单元测试
│ └── dm/
│ └── client.rs # ✅ 保留单元测试
└── tests/
├── integration_dm.rs # DM 功能集成测试
├── integration_auth.rs # 认证流程集成测试
└── common/
└── mod.rs # 测试辅助函数(不会被当作测试文件)
单元测试¶
运行单元测试¶
# 运行所有单元测试
cargo test
# 运行特定模块的测试
cargo test upload::types::tests
cargo test upload::client::tests
# 带日志输出的测试
RUST_LOG=debug cargo test -- --nocapture
单元测试覆盖¶
src/upload/types.rs - 9 个测试¶
测试类型定义、序列化、Debug 格式化:
test_media_category_as_str: 测试 MediaCategory 枚举转字符串test_media_category_clone: 测试 Clone traittest_upload_result_success: 测试成功上传结果test_upload_result_failure: 测试失败上传结果test_upload_result_clone: 测试 Clone traittest_upload_init_response_deserialization: 测试 JSON 反序列化test_upload_init_response_serialization: 测试 JSON 序列化test_upload_result_debug_format: 测试 Debug 格式化test_media_category_all_variants: 测试所有枚举变体
src/upload/client.rs - 4 个测试¶
测试 MD5 计算、数据处理:
test_md5_known_values: 测试已知 MD5 值test_md5_format_length: 测试 MD5 输出格式test_image_size_calculation: 测试图片大小计算test_md5_binary_data: 测试二进制数据 MD5
其他模块¶
- ✅
src/common/auth.rs: Cookie 解析和认证测试 - ✅
src/dm/client.rs: 私信发送逻辑测试
集成测试¶
配置集成测试环境¶
集成测试需要真实的 Twitter cookies。
步骤 1:复制模板文件
步骤 2:编辑文件,填入真实的 cookies
格式: ct0=xxx; auth_token=yyy; twid=u%3D123
步骤 3:运行集成测试
# 运行所有集成测试(需要真实 cookies)
cargo test -- --ignored
# 运行特定的集成测试
cargo test test_upload_tweet_image -- --ignored
cargo test test_upload_dm_image -- --ignored
cargo test test_upload_banner_image -- --ignored
集成测试覆盖¶
tests/integration_upload.rs - 图片上传完整流程测试¶
test_upload_tweet_image: 推文图片上传(需要真实 cookies)test_upload_dm_image: 私信图片上传(需要真实 cookies)test_upload_banner_image: 横幅图片上传(需要真实 cookies)test_upload_multiple_images: 批量上传测试(需要真实 cookies)test_cookies_validation: Cookies 验证(不需要真实 cookies)test_media_category_enum: 媒体类别枚举测试(不需要真实 cookies)
tests/integration_dm_with_media.rs - 带图片私信完整流程测试¶
test_send_dm_with_image: 上传图片 + 发送带图片私信(需要真实 cookies)test_send_dm_without_image: 发送纯文本私信,验证向后兼容(需要真实 cookies)test_batch_send_dm_with_mixed_media: 批量发送混合私信(部分带图片)(需要真实 cookies)test_batch_send_dm_all_with_images: 批量发送全部带图片(需要真实 cookies)test_media_ids_validation: 参数验证测试(不需要真实 cookies)
测试辅助模块¶
tests/common/mod.rs - 提供测试辅助函数¶
// 创建最小的测试 JPEG 图片
pub fn create_test_jpeg() -> Vec<u8>
// 创建测试用 cookies
pub fn create_test_cookies() -> String
// 验证上传成功
pub fn assert_upload_success(result: &UploadResult)
// 验证上传失败
pub fn assert_upload_failure(result: &UploadResult)
测试输出规范¶
强制要求¶
- 禁止使用
println!,print!,eprintln!,eprint!进行测试输出 - 必须使用
tracingcrate 的日志宏:debug!,info!,warn!,error!
测试日志初始化¶
单元测试¶
#[cfg(test)]
mod tests {
use super::*;
fn init_test_logging() {
let _ = tracing_subscriber::fmt()
.with_test_writer()
.with_max_level(tracing::Level::DEBUG)
.try_init();
}
#[test]
fn test_example() {
init_test_logging();
info!("开始测试");
debug!("详细信息: {:?}", data);
}
}
集成测试¶
// tests/common/mod.rs
pub fn init_test_logging() {
let _ = tracing_subscriber::fmt()
.with_test_writer()
.with_max_level(tracing::Level::DEBUG)
.try_init();
}
// tests/integration_upload.rs
#[tokio::test]
async fn test_upload() {
common::init_test_logging();
info!("测试开始");
}
测试输出示例¶
// ✅ 正确
#[test]
fn test_batch_upload() {
init_test_logging();
info!("📸 测试图片大小: {} bytes", image_bytes.len());
info!("🚀 开始批量上传 {} 次...", count);
info!("✅ 批量上传完成!");
debug!("详细结果: {:?}", result);
}
// ❌ 错误
#[test]
fn test_batch_upload() {
println!("📸 测试图片大小: {} bytes", image_bytes.len()); // 禁止
println!("🚀 开始批量上传 {} 次...", count); // 禁止
}
代码提交前检查清单¶
在提交代码前,确保:
测试组织¶
- [ ] 单元测试保留在
src/对应文件中 - [ ] 集成测试按模块组织在
tests/<module>/目录 - [ ] 测试辅助函数放在
tests/common/mod.rs
测试输出¶
- [ ] 测试中无
println!或print! - [ ] 所有测试输出使用
tracing日志宏 - [ ] 测试日志已正确初始化
测试覆盖¶
- [ ] 新功能添加了单元测试
- [ ] 复杂场景添加了集成测试
- [ ] 测试覆盖了边界条件和错误处理
测试命名规范¶
- 单元测试:
test_功能描述(如test_cookie_auth_parse) - 集成测试:
integration_模块名(如integration_dm.rs) - 使用描述性名称,说明测试意图而非实现细节
测试编写原则¶
- 单元测试: 测试边界条件、错误处理、私有实现细节
- 集成测试: 测试端到端流程、公共 API 使用、模块协作
- 测试隔离: 每个测试独立运行,互不依赖
- 快速执行: 单元测试应快速完成,集成测试可选择性运行
禁止的做法¶
- ❌ 在
lib.rs中放置复杂的测试逻辑 - ❌ 在单元测试中测试跨模块的协作
- ❌ 在集成测试中依赖私有 API
注意事项¶
- 🚫 默认跳过集成测试:集成测试标记为
#[ignore],需要真实 cookies 才能运行 - 🔐 Cookies 配置:通过
tests/.twitter_cookies.txt文件传递认证信息 - ✅ 独立运行:单元测试可以独立运行,不依赖外部服务
- 📊 测试覆盖:单元测试 18 个,集成测试 11 个
- 🔒 安全性:
.gitignore已配置排除*cookies*.txt,防止意外提交敏感信息
相关文档¶
- CLAUDE.md - 项目规范和架构设计
- API.md - Twitter API 详细规格
- LOGGING.md - 日志使用规范
- DEVELOPMENT.md - 开发指南