Skip to content

Latest commit

 

History

History
434 lines (319 loc) · 9.35 KB

File metadata and controls

434 lines (319 loc) · 9.35 KB

OpenLark 测试指南

本文档提供 OpenLark SDK 测试的完整指南,包括快速开始、最佳实践和工具使用。

📚 目录


快速开始

运行所有测试

# 运行工作区所有测试
cargo test --workspace

# 运行特定 crate 的测试
cargo test -p openlark-core
cargo test -p openlark-auth
cargo test -p openlark-webhook

运行特定测试

# 运行匹配名称的测试
cargo test test_name

# 运行特定模块的测试
cargo test --lib validation

# 运行集成测试
cargo test --test integration_webhook

快速检查

# 格式检查
cargo fmt --check

# Lint 检查
cargo clippy -- -D warnings

# 完整检查(格式 + Lint + 测试)
just check-all

测试组织

目录结构

.
├── crates/
│   └── openlark-*/tests/     # 每个 crate 的集成测试
├── tests/
│   ├── integration/           # 跨 crate 集成测试
│   ├── property/              # 属性测试
│   └── live/                  # 真实环境测试(需要 .env)
└── benches/                   # 性能基准测试

测试类型

类型 位置 特点
单元测试 src/**/mod.rs (#[cfg(test)]) 测试内部逻辑
集成测试 crates/*/tests/ 测试公共 API
属性测试 tests/property/ 随机输入验证
Live 测试 tests/live/ 真实环境(.env)

测试工具

TestServer(推荐)

统一的 HTTP Mock 服务器封装。

use openlark_core::testing::prelude::*;
use serde_json::json;

#[tokio::test]
async fn test_example() {
    let server = TestServer::new().await;
    
    // Mock 成功响应
    server.mock_success("/api/v1/test", json!({"code": 0})).await;
    
    // Mock 错误响应
    server.mock_error("/api/v1/error", 400, json!({"code": 99991663})).await;
    
    // Mock 超时
    server.mock_timeout("/api/v1/slow", std::time::Duration::from_secs(10)).await;
    
    // 使用 server.uri() 作为 base_url
    let config = test_config(&server.uri());
    
    // 测试代码...
}

API 参考

  • mock_success(route, body) - 200 OK 响应
  • mock_error(route, code, error) - 4xx/5xx 错误
  • mock_timeout(route, delay) - 超时模拟
  • mock_get/put/delete() - 其他 HTTP 方法
  • mock_with_verification(route, expected, response) - 带请求体验证

测试配置

use openlark_core::testing::prelude::*;

// 快速创建测试配置
let config = test_config("http://localhost:8080");

// 自定义配置
let config = TestConfigBuilder::new()
    .app_id("custom_app_id")
    .app_secret("custom_secret")
    .base_url("http://custom.url")
    .timeout(Duration::from_secs(30))
    .build();

断言宏

use openlark_core::testing::prelude::*;

// Result 断言
let result = some_operation().await;
let value = assert_res_ok!(result, "operation should succeed");
assert_eq!(value.id, "123");

// 错误断言
let result = some_failing_operation().await;
assert_res_err!(result, CoreError::Validation { .. }, "should fail validation");

// 错误消息断言
let err = assert_err_contains!(result, "不能为空", "field validation");

// Option 断言
let opt = Some(42);
let value = assert_some!(opt, "should have value");
assert_none!(opt_none, "should be None");

参数化测试(rstest)

use rstest::rstest;

#[rstest]
#[case::success("valid_id", true)]
#[case::empty("", false)]
#[case::whitespace("  ", false)]
#[tokio::test]
async fn test_validation(
    #[case] input: &str,
    #[case] should_pass: bool,
) {
    let result = validate_input(input);
    
    if should_pass {
        assert!(result.is_ok());
    } else {
        assert!(result.is_err());
    }
}

覆盖率

生成覆盖率报告

# 工作区覆盖率
just coverage

# 特定 crate
cargo llvm-cov -p openlark-core --html
cargo llvm-cov -p openlark-auth --html

# 查看缺失行
cargo llvm-cov --workspace --show-missing-lines

覆盖率报告位置

  • HTML 报告target/llvm-cov/html/index.html
  • LCOV 格式target/llvm-cov/lcov.info
  • Per-crate 报告target/llvm-cov/crates/*/

覆盖率门禁

指标 阈值 说明
全局 40% main 分支强制
openlark-core 40% 警告
openlark-client 35% 警告
openlark-auth 50% 警告

查看 CI 覆盖率

  1. 打开 PR 或 Push 的 Actions 页面
  2. 查看 "Coverage" job 的 Step Summary
  3. 下载 "coverage-report" artifact 查看 HTML 报告

最佳实践

1. 使用 TestServer 统一 Mock

推荐

let server = TestServer::new().await;
server.mock_success("/api/v1/test", json!({"code": 0})).await;

避免

let server = MockServer::start().await;
Mock::given(method("POST"))
    .and(path("/api/v1/test"))
    .respond_with(ResponseTemplate::new(200).set_body_json(json!({...})))
    .mount(&server)
    .await;

2. 测试三元组(每个 API)

对于每个 API 端点,至少测试:

  1. 成功路径:正常输入,200 OK
  2. 参数校验:空值、边界值、非法值
  3. 错误映射:4xx/5xx 响应
#[tokio::test]
async fn test_create_user_success() {
    // 成功路径
    let server = TestServer::new().await;
    server.mock_success("/api/v1/users", json!({"code": 0, "data": {"id": "123"}})).await;
    
    let result = create_user("valid_name").await;
    assert_res_ok!(result, "create_user");
}

#[tokio::test]
async fn test_create_user_validation() {
    // 参数校验
    let result = create_user("").await;
    assert_res_err!(result, CoreError::Validation { .. }, "empty name");
}

#[tokio::test]
async fn test_create_user_api_error() {
    // 错误映射
    let server = TestServer::new().await;
    server.mock_error("/api/v1/users", 400, json!({"code": 99991663, "msg": "invalid"})).await;
    
    let result = create_user("valid_name").await;
    assert_res_err!(result, CoreError::Api { .. }, "api error");
}

3. 模型序列化测试

#[test]
fn test_user_serialization_roundtrip() {
    let user = User {
        id: "123".to_string(),
        name: "张三".to_string(),
    };
    
    let json = serde_json::to_string(&user).expect("serialize");
    let decoded: User = serde_json::from_str(&json).expect("deserialize");
    
    assert_eq!(decoded.id, user.id);
    assert_eq!(decoded.name, user.name);
}

4. Feature-gated 测试

#[cfg(feature = "websocket")]
#[tokio::test]
async fn test_websocket_connection() {
    // 仅在启用 websocket feature 时运行
}

#[tokio::test]
async fn test_always_runs() {
    // 总是运行
}

5. Live 测试隔离

#[tokio::test]
#[ignore = "需要设置 OPENLARK_LIVE_TESTS=1"]
async fn test_live_auth() {
    if std::env::var("OPENLARK_LIVE_TESTS").is_err() {
        println!("⚠️  跳过 live 测试");
        return;
    }
    
    dotenvy::dotenv().ok();
    let app_id = std::env::var("APP_ID").expect("APP_ID must be set");
    
    // 真实环境测试...
}

故障排查

测试失败

问题:测试偶发失败(网络/时序相关)

解决方案

  1. 使用 TestServer Mock(不依赖外部网络)
  2. 增加超时时间
  3. 使用 serial_test 避免并发冲突
use serial_test::serial;

#[tokio::test]
#[serial]  // 串行执行
async fn test_concurrent_unsafe() {
    // ...
}

覆盖率报告不准确

问题:覆盖率报告显示 0% 或不准确

解决方案

# 清理覆盖率数据
cargo llvm-cov clean --workspace

# 重新生成
cargo llvm-cov --workspace --all-features --html

Feature 组合测试失败

问题:某些 feature 组合编译失败

解决方案

# 检查特定 feature
cargo check -p openlark-core --no-default-features
cargo check -p openlark-core --features websocket

# 运行 feature 矩阵测试
just test-features-quick

找不到测试

问题cargo test 没有运行某些测试

解决方案

# 检查测试是否被 ignore
cargo test -- --include-ignored

# 检查特定测试文件
cargo test --test integration_webhook

# 列出所有测试
cargo test -- --list

开发者清单

每次添加新 API 时,确保:

  • 至少 3 个测试(成功 + 校验 + 错误)
  • 模型序列化/反序列化测试
  • 使用 TestServer Mock
  • 参数验证测试(使用 validate_required! 宏)
  • Feature-gated 测试(如适用)
  • 覆盖率 >= crate 平均水平

相关文档


快速参考

命令 说明
cargo test 运行所有测试
just coverage 生成覆盖率报告
just test-features-quick Feature 组合测试
just check-all 完整质量检查
cargo llvm-cov --html HTML 覆盖率报告

问题反馈:如果遇到问题,请在 GitHub Issues 提交。