8 个版本

0.2.1 2024 年 5 月 15 日
0.1.6 2024 年 5 月 12 日
0.1.5 2024 年 4 月 17 日
0.1.4 2023 年 8 月 21 日

#97 in 测试

Download history 3/week @ 2024-04-27 326/week @ 2024-05-11 29/week @ 2024-05-18 10/week @ 2024-05-25 10/week @ 2024-06-01 7/week @ 2024-06-08 2/week @ 2024-06-15 16/week @ 2024-07-13 39/week @ 2024-07-20 30/week @ 2024-07-27 21/week @ 2024-08-03 13/week @ 2024-08-10

每月 104 次下载

Apache-2.0 OR MIT

96KB
2K SLoC

Crates.io docs.rs pipeline status coverage report license dependency status lines of code

rtest - 基于资源的测试框架

Rust 中有许多 单元测试 框架。本框架专注于集成测试,这意味着外部软件,不一定是用 Rust 编写的。

rtest 通过使用有状态的资源来工作。它使用宏来构建一个可执行的二进制文件,可以处理所有过滤器并返回一个良好的输出。

想象你正在运营一个网店,并想通过集成测试来验证它是否正常工作。

#[derive(rtest_derive::Resource)]
struct Orderinfo {
    item: String,
}
#[derive(rtest_derive::Resource)]
struct Order(String);

#[derive(Debug, thiserror::Error)]
pub enum ShopError {
    #[error("{0}")]
    Network(#[from] reqwest::Error),
}
impl rtest::TestError for ShopError {}

const SHOP: &str = "http://shop.example.com";

#[rtest_derive::rtest]
async fn place_order(info: Orderinfo) -> Result<Order, ShopError> {
    let client = reqwest::Client::new();
    let url = format!("{}/v1/order/{}", SHOP, info.item);
    let id = client.post(url).send().await?.text().await?;
    Ok(Order(id))
}

#[rtest_derive::rtest]
async fn check_order(order: Order) -> Result<Order, ShopError> {
    let res = reqwest::get(format!("{}/v1/order/{}", SHOP, order.0)).await?;
    assert_ne!(res.status(), 404);
    Ok(order)
}

#[rtest_derive::rtest]
async fn cancel_order(order: Order) -> Result<(), ShopError> {
    let client = reqwest::Client::new();
    let res = client.delete(format!("{}/v1/order/{}", SHOP, order.0)).send().await?;
    assert_eq!(res.status(), 200);
    Ok(())
}

pub fn main() -> std::process::ExitCode {
    let water = Orderinfo {
        item: "water".to_string(),
    };
    let pizza = Orderinfo {
        item: "pizza".to_string(),
    };
    let runconfig = rtest::RunConfig {
        context: rtest::Context::default().with_resource(water).with_resource(pizza),
        ..Default::default()
    };
    rtest_derive::run!(runconfig)
}

测试框架将知道为了测试 check_order 函数,它首先需要有一个 Order。但是生成此类订单的唯一方法是通过 place_order 测试。 cancel_order 将消耗订单,使其不再可用。

是的,你可以通过删除所有生成 Order 的测试来欺骗它 - 框架将在运行时注意到这一点并失败。可能存在多个路径可以测试所有函数,如果出现错误,则将输出所采取的路径。假设使用 water 检查订单会失败。框架可能会决定创建另一个带有 pizza 的订单,因为它无法验证删除,测试可能会多次执行。

rtest Results:
[]
  [] delete_file
[] create
  [] create_file
  [] setup_fileinfo
[x] read
  [] optional_test
  [] read_metadata
  [x] test_that_should_fail
      --- Run: 1/1 ---
      Error: test failure: No such file or directory (os error 2)
      Logs:
        2024-05-14T10:11:59.824647Z  INFO filesystem: Wubba Lubba dub-dub
  [x] test_that_should_panic - 183ms
      --- Run: 1/1 ---
      Error: Panic: 'Yes Rico, Kaboom'
      at rtest/examples/filesystem/main.rs:88
      Stacktrace:
         0: rust_begin_unwind
                   at /rustc/098d4fd74c078b12bfc2e9438a2a04bc18b393bc/library/std/src/panicking.rs:647:5
         1: core::panicking::panic_fmt
                   at /rustc/098d4fd74c078b12bfc2e9438a2a04bc18b393bc/library/core/src/panicking.rs:72:14
         2: filesystem::test_that_should_panic
                   at rtest/examples/filesystem/main.rs:88:5
      Logs:
        2024-05-14T10:11:59.641008Z  INFO filesystem: Kaboom?
Total Tests: 7. Total Runs: 6 Errors: 2
Failed

示例

执行网店,其中一个测试标记为可选,重新运行并显示结果

cargo run --example=webshop -- run --optional-tests=run_test_on_2_servers -o rtest_result.json
cargo run --example=webshop -- re-run rtest_result.json
cargo run --example=webshop -- display rtest_result.json

功能

  • 允许任何输入/输出资源
  • 自定义错误
  • 自定义上下文(尽管很少需要)
  • 考虑(过程/资源的)成本的执行模型
  • 多线程支持
  • 异步支持
  • 捕获日志
  • 捕获恐慌(对于断言而言是必需的)
  • 捕获 println
  • 外部日志捕获,例如通过适配器在测试执行期间捕获 Kubernetes 容器的日志。
  • Json 输入/输出到持久运行、重试运行、与之前的运行进行比较
  • Markdown 输出

依赖关系

~4–18MB
~193K SLoC