#testing #tokio #async #attributes #macro #guard #write

test-with-tokio

用于使用Tokio、案例和异步守卫进行测试的属性宏

7个版本

0.3.3 2023年1月9日
0.3.2 2023年1月8日
0.2.1 2022年12月31日
0.1.0 2022年12月23日

#16 in #guard

MIT 许可证

8KB

Workflow Status

这是一个属性宏,用于使用Tokio进行测试,具有案例和异步守卫。

这个crate提供了一个简单的属性宏 #[test_with_tokio::please],允许你编写在Tokio中运行异步代码之前先运行一些非异步代码的测试。这类似于 #[tokio::test],但有两大特性:可以在启动Tokio运行时之前运行异步代码,并且可以编写一个测试来生成多个测试,这些测试处理相同测试的多个案例。通过一些工作,这可以使你并行运行大多数测试,但有一些测试不能并行运行。

示例

在最基本的情况下,这个crate使你能够轻松编写在运行异步代码之前先运行非异步代码的测试。

// The async in `async fn` below is optional and ignored.
#[test_with_tokio::please]
async fn test_me() {
    println!("This code will be run before the tokio runtime is started.");
    async_std::println!("This code will be run with a tokio runtime").await;
}

持有锁

这个crate的动机是允许使用锁来并行运行测试

static DIRECTORY_LOCK: std::sync::RwLock<()> = std::sync::RwLock::new(());

#[test_with_tokio::please]
fn test_run_exclusively() {
    let _guard = DIRECTORY_LOCK.write().unwrap();
    async_std::println!("This code will be run with exclusive access to the directory.").await;
}

#[test_with_tokio::please] fn test_run_cooperatively() {
    let _guard = DIRECTORY_LOCK.read().unwrap();
    async_std::println!("This code will be run concurrently with other cooperative tests..").await;
}

你可能想知道,为什么不在 async 块内获取锁,或者也许只是在一个带有 #[tokio::test] 标记的函数内简单地获取锁?答案是缺少 async Drop。这意味着测试可能不会在Tokio运行时退出后才完全清理,这意味着你仍然可能在测试中遇到竞态条件,同时并发获取锁。

多个案例

如果你可以编写通过将变量分配给 match CASE { ... } 生成多个相关测试的代码,其中每个案例都匹配一个有效的标识符后缀的字符串字面量。

#[test_with_tokio::please]
fn test_contains() {
    let container = match CASE {
        "hello" => "hello world",
        "this_test" => vec!["this_test"],
    };
    assert!(container.contains(CASE));
}

此示例将创建两个函数,每个函数都带有 #[test] 标记,一个命名为 test_contains_hello,另一个 test_contains_this_test。第一个函数的主体将类似于

#[test]
fn test_contains_hello() {
    const CASE: &str = "hello";
    let container = "hello world";
    assert!(container.contains(CASE));
}

依赖

~1.5MB
~35K SLoC