Rust测试辅助库test-with的使用:简化单元测试与集成测试的编写与执行

Rust测试辅助库test-with的使用:简化单元测试与集成测试的编写与执行

test-with是一个帮助你在特定条件下运行测试的库,如果条件不满足则会清晰地显示忽略信息。它还能让你更轻松地使用自定义测试环境或模拟服务运行测试。

安装

在项目的dev-dependencies中添加:

[dev-dependencies]
test-with = "*"

如果要减少依赖大小和编译时间,可以禁用默认功能并只启用特定功能:

[dev-dependencies]
test-with = { version = "*", default-features = false, features = ["http"] }

可用功能包括:net(http, icmp), resource, user, executable

环境变量测试

// PWD环境变量存在时运行测试
#[test_with::env(PWD)]
#[test]
fn test_works() {
    assert!(true);
}

// NOTHING环境变量不存在时运行测试
#[test_with::env(NOTHING)]
#[test]
fn test_ignored() {
    panic!("should be ignored")
}

完整示例:

#[test_with::env(PWD)]
#[test]
fn test_with_env_var() {
    // 这个测试只会在PWD环境变量存在时运行
    assert!(true);
}

#[test_with::no_env(GITHUB_ACTIONS)]
#[test]
fn test_ignore_in_github_action() {
    // 这个测试在GITHUB_ACTIONS环境变量存在时会被忽略
    println!("Should be ignored in GITHUB_ACTION");
}

文件/文件夹测试

// /etc/hostname文件存在时运行测试
#[test_with::file(/etc/hostname)]
#[test]
fn test_works() {
    assert!(true);
}

// /etc/nothing文件不存在时运行测试
#[test_with::file(/etc/nothing)]
#[test]
fn test_ignored() {
    panic!("should be ignored")
}

// /etc目录存在时运行测试
#[test_with::path(/etc)]
#[test]
fn test_works_for_path() {
    assert!(true);
}

完整示例:

#[test_with::file(Cargo.toml)]
#[test]
fn test_with_file() {
    // 这个测试只会在Cargo.toml文件存在时运行
    assert!(true);
}

#[test_with::path(src)]
#[test]
fn test_with_directory() {
    // 这个测试只会在src目录存在时运行
    assert!(true);
}

HTTP/HTTPS服务测试

// https服务存在时运行测试
#[test_with::https(www.rust-lang.org)]
#[test]
fn test_works() {
    assert!(true);
}

// not.exist.com不存在时运行测试
#[test_with::https(not.exist.com)]
#[test]
fn test_ignored() {
    panic!("should be ignored")
}

完整示例:

#[test_with::http(localhost:8080)]
#[test]
fn test_with_http_service() {
    // 这个测试只会在localhost:8080 HTTP服务可用时运行
    assert!(true);
}

#[test_with::https(api.example.com)]
#[test]
fn test_with_https_service() {
    // 这个测试只会在api.example.com HTTPS服务可用时运行
    assert!(true);
}

TCP套接字测试

// TCP端口53可用时运行测试
#[test_with::tcp(8.8.8.8:53)]
#[test]
fn test_works() {
    assert!(true);
}

// 193.194.195.196不可用时运行测试
#[test_with::tcp(193.194.195.196)]
#[test]
fn test_ignored() {
    panic!("should be ignored")
}

完整示例:

#[test_with::tcp(127.0.0.1:6379)]
#[test]
fn test_with_redis() {
    // 这个测试只会在本地Redis服务(6379端口)可用时运行
    assert!(true);
}

运行时测试

启用runtime功能:

test-with = { version = "0.15.0", features = ["runtime"] }
libtest-with = { version = "0.8.1-10", features = ["net", "resource", "user", "executable"] }

示例:

test_with::runner!(module_name);

#[test_with::module]
mod module_name {
    #[test_with::runtime_env(PWD)]
    fn test_works() {
    }
}

完整示例:

test_with::runner!(custom_mod);

fn something_happened() -> Option<String> {
    Some("because something happened".to_string())
}

#[test_with::module]
mod custom_mod {
    #[test_with::runtime_ignore_if(something_happened)]
    fn test_ignored() {
        assert!(false);
    }
}

锁测试

// 使用文件锁确保测试顺序执行
#[test_with::lock(LOCK)]
fn test_1() {
    assert!(true);
}

#[test_with::lock(LOCK)]
fn test_2() {
    assert!(true);
}

完整示例:

#[test_with::lock(DATABASE_LOCK, 10)]  // 10秒等待时间
#[test]
fn test_database_operation1() {
    // 这个测试会获取DATABASE_LOCK锁
    assert!(true);
}

#[test_with::lock(DATABASE_LOCK, 10)]
#[test]
fn test_database_operation2() {
    // 这个测试会等待前一个测试释放锁
    assert!(true);
}

时区测试

// UTC时区运行测试
#[test_with::timezone(0)]
#[test]
fn test_works() {
    assert!(true);
}

// GMT+8时区运行测试
#[test_with::timezone(+8)]
#[test]
fn test_run_in_TW() {
    assert!(true)
}

完整示例:

#[test_with::timezone(+8)]
#[test]
fn test_in_taiwan_timezone() {
    // 这个测试只会在GMT+8时区运行
    assert!(true);
}

用户/组条件测试

#[test_with::root()]
#[test]
fn test_ignored() {
    panic!("should be ignored")
}

#[test_with::group(avengers)]
#[test]
fn test_ignored2() {
    panic!("should be ignored")
}

#[test_with::user(spider)]
#[test]
fn test_ignored3() {
    panic!("should be ignored")
}

完整示例:

#[test_with::user(admin)]
#[test]
fn test_admin_only() {
    // 这个测试只会在admin用户下运行
    assert!(true);
}

可执行文件测试

// `pwd`命令存在时运行测试
#[test_with::executable(pwd)]
#[test]
fn test_executable() {
    assert!(true);
}

// `/bin/sh`存在时运行测试
#[test_with::executable(/bin/sh)]
#[test]
fn test_executable_with_path() {
    assert!(true);
}

完整示例:

#[test_with::executable(docker)]
#[test]
fn test_with_docker() {
    // 这个测试只会在docker命令可用时运行
    assert!(true);
}

test-with库提供了多种条件测试方式,可以大大简化Rust项目中单元测试和集成测试的编写与执行,特别是在需要特定环境或依赖服务的测试场景中。


1 回复

Rust测试辅助库test-with的使用:简化单元测试与集成测试的编写与执行

test-with是一个Rust测试辅助库,旨在简化单元测试和集成测试的编写与执行过程。它提供了一些实用功能来管理测试环境、处理测试数据以及控制测试执行流程。

主要特性

  1. 简化测试函数的编写
  2. 支持测试环境设置和清理
  3. 提供参数化测试支持
  4. 改进测试输出和报告

安装方法

在Cargo.toml中添加依赖:

[dev-dependencies]
test-with = "0.1"

基本使用方法

简单测试示例

use test_with::test_with;

#[test_with]
fn test_addition() {
    assert_eq!(2 + 2, 4);
}

带环境的测试

use test_with::test_with;

struct TestEnv {
    value: i32,
}

#[test_with(setup = setup_env, teardown = teardown_env)]
fn test_with_environment() {
    let env = TestEnv::current();
    assert_eq!(env.value, 42);
}

fn setup_env() -> TestEnv {
    TestEnv { value: 42 }
}

fn teardown_env(env: TestEnv) {
    // 清理资源
}

参数化测试

use test_with::test_with;

#[test_with(params = [1, 2, 3, 4])]
fn test_even_numbers(param: i32) {
    assert_eq!(param % 2, 0);
}

异步测试支持

use test_with::test_with;
use tokio::runtime::Runtime;

#[test_with(runtime = Runtime::new().unwrap())]
async fn test_async_function() {
    let result = some_async_function().await;
    assert!(result.is_ok());
}

测试分组

use test_with::{test_with, TestGroup};

#[test_with(group = "database")]
fn test_db_connection() {
    // 测试数据库连接
}

#[test_with(group = "database")]
fn test_db_query() {
    // 测试数据库查询
}

// 可以只运行特定分组的测试
// cargo test -- --group database

高级功能

测试依赖

use test_with::test_with;

#[test_with(depends = ["setup_data"])]
fn test_using_data() {
    // 这个测试会在setup_data测试完成后运行
}

#[test_with]
fn setup_data() {
    // 准备测试数据
}

条件测试

use test_with::test_with;

#[test_with(skip_if = cfg!(windows))]
fn test_unix_only() {
    // 这个测试不会在Windows上运行
}

配置选项

可以在项目根目录下创建test-with.toml文件来配置全局测试行为:

[default]
timeout = 30 # 默认测试超时时间(秒)
report = "detailed" # 测试报告格式

完整示例demo

下面是一个结合多个功能的完整示例:

// 引入test-with宏
use test_with::{test_with, TestGroup};
use tokio::runtime::Runtime;

// 测试环境结构体
struct DatabaseTestEnv {
    connection_string: String,
    connected: bool,
}

// 简单测试示例
#[test_with]
fn test_simple_math() {
    assert_eq!(3 * 3, 9);
}

// 带环境的测试
#[test_with(setup = setup_db_env, teardown = teardown_db_env)]
fn test_database_connection() {
    let env = DatabaseTestEnv::current();
    assert!(env.connected);
    assert!(!env.connection_string.is_empty());
}

fn setup_db_env() -> DatabaseTestEnv {
    DatabaseTestEnv {
        connection_string: "test_db://localhost".to_string(),
        connected: true,
    }
}

fn teardown_db_env(env: DatabaseTestEnv) {
    println!("清理数据库连接: {}", env.connection_string);
}

// 参数化测试
#[test_with(params = [2, 4, 6, 8])]
fn test_even_numbers(param: i32) {
    assert_eq!(param % 2, 0);
}

// 异步测试
#[test_with(runtime = Runtime::new().unwrap())]
async fn test_async_operation() {
    let result = async { Ok::<_, String>(42) }.await;
    assert_eq!(result.unwrap(), 42);
}

// 测试分组
#[test_with(group = "database")]
fn test_db_query() {
    // 模拟数据库查询测试
    assert!(true);
}

// 测试依赖
#[test_with]
fn setup_test_data() {
    println!("准备测试数据");
}

#[test_with(depends = ["setup_test_data"])]
fn test_using_prepared_data() {
    println!("使用预先准备的数据进行测试");
}

// 条件测试
#[test_with(skip_if = cfg!(windows))]
fn test_linux_specific_feature() {
    // 这个测试不会在Windows上运行
    println!("测试Linux特有功能");
}

这个完整示例展示了test-with库的主要功能:

  1. 简单测试
  2. 带环境设置和清理的测试
  3. 参数化测试
  4. 异步测试支持
  5. 测试分组
  6. 测试依赖管理
  7. 条件测试

要运行这些测试,只需执行常规的cargo test命令,或者使用cargo test -- --group database来只运行特定分组的测试。

回到顶部