46 个版本

0.13.0 2024年7月11日
0.12.6 2024年3月5日
0.12.5 2024年2月15日
0.12.2 2023年12月22日
0.1.0 2021年12月31日

#176开发工具

Download history 4546/week @ 2024-04-26 4197/week @ 2024-05-03 5093/week @ 2024-05-10 5481/week @ 2024-05-17 6409/week @ 2024-05-24 5324/week @ 2024-05-31 5658/week @ 2024-06-07 6149/week @ 2024-06-14 5051/week @ 2024-06-21 4038/week @ 2024-06-28 5066/week @ 2024-07-05 5211/week @ 2024-07-12 5320/week @ 2024-07-19 5143/week @ 2024-07-26 5336/week @ 2024-08-02 4947/week @ 2024-08-09

每月下载量 21,716
用于 10 crates

MIT 许可证

115KB
2.5K SLoC

test-with

Crates.io MIT licensed Docs

一个库,帮助你根据条件运行测试,否则测试将被忽略并显示清晰的消息。你还可以更容易地使用自定义测试环境或模拟服务运行测试。

简介

建议将此库作为开发依赖项使用,如下所示

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

如果你希望依赖项更小,编译时间更短,可以禁用默认功能并使用特定功能。例如,如果你只检查远程网络服务器,你可以使用以下 nethttp 功能。

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

你可以使用以下功能: net(http, icmp),resourceuserexecutable

目前,条件检查是在构建时而不是运行时进行的,并不完美,但对于大多数开发场景来说已经足够好了。这是因为rust-lang的这个issue。这里有COSCUP幻灯片COSCON幻灯片可以帮助你了解更多信息。如果你真的想在运行时检查条件,请查看运行时部分。运行时功能(runtime)和运行时宏(test_with::runner!#[test_with::module]#[test_with::runtime_env()])可以帮助你在运行时运行测试和检查条件。此外,可以使用带有runtime功能的模块设置自定义测试环境或模拟服务。

如果你忘记在测试用例上添加#[test]标志,#[test_with]宏会为你添加它。

稳定通道的Rust版本1.61或夜间通道的2022-03-30将显示忽略信息。如果之前使用的Rust版本中没有显示忽略信息,可以使用功能ign-msg来解决这个问题,并且被忽略的测试用例的名称将被重写,这样你可以更容易地知道为什么测试被忽略。

测试宏的顺序(#[test]#[tokio::test]#[serial_test::serial]#[rstest]...)很重要,请查看示例。

环境变量

当设置环境变量时运行测试用例。

// PWD environment variable exists
#[test_with::env(PWD)]
#[test]
fn test_works() {
    assert!(true);
}

// NOTHING environment variable does not exist
#[test_with::env(NOTHING)]
#[test]
fn test_ignored() {
    panic!("should be ignored")
}

cargo test的结果

running 2 tests
test tests::test_ignored ... ignored, because following variable not found: NOTHING
test tests::test_works ... ok

test result: ok. 1 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 0.00s

或者当设置环境变量时运行测试模块的所有测试用例。

#[test_with::env(PWD)]
#[cfg(test)]
mod tests {

    #[test]
    fn test_works() {
        assert!(true);
    }
}

如果测试依赖于多个环境变量,你可以用多个变量来编写它,#[test_with::env(VAR1, VAR2)]

此外,也可以使用特定的环境变量忽略测试用例。

// The test will be ignored in Github actions.
#[test_with::no_env(GITHUB_ACTIONS)]
#[test]
fn test_ignore_in_github_action() {
    println!("Should be ignored in GITHUB_ACTION");
}

文件/文件夹

当文件或文件夹存在时运行测试用例。这对于与数据库配置进行测试很有用。如果你想检查文件夹是否存在,请使用path

// hostname exists
#[test_with::file(/etc/hostname)]
#[test]
fn test_works() {
    assert!(true);
}

// nothing file does not exist
#[test_with::file(/etc/nothing)]
#[test]
fn test_ignored() {
    panic!("should be ignored")
}

// etc exists
#[test_with::path(/etc)]
#[test]
fn test_works_for_path() {
    assert!(true);
}

如果测试依赖于多个文件或路径,可以使用多个文件/路径来编写它,例如:#[test_with::file(/file1, /file2)]#[test_with::path(/folder, /file)]

Http/Https 服务

当 http/https 服务可用时运行测试用例。这适用于集成测试。如果默认功能被禁用,则需要 http 功能。

// https service exists
#[test_with::https(www.rust-lang.org)]
#[test]
fn test_works() {
    assert!(true);
}

// There is no not.exist.com
#[test_with::https(not.exist.com)]
#[test]
fn test_ignored() {
    panic!("should be ignored")
}

如果测试依赖于多个服务,可以使用多个服务来编写它,例如:#[test_with::http(service1, service2)]#[test_with::http2(service1, service2)]

TCP 套接字

当远程 TCP 套接字正在监听时运行集成测试用例。

#[test_with::tcp(8.8.8.8:53)]
#[test]
fn test_works() {
    assert!(true);
}

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

远程服务器在线状态

当远程服务器在线时运行集成测试用例。**请注意,运行测试用例的用户应具有打开套接字的能力**。如果默认功能被禁用,则需要 icmp 功能。

// localhost is online
#[test_with::icmp(127.0.0.1)]
#[test]
fn test_works() {
    assert!(true);
}

// 193.194.195.196 is offline
#[test_with::icmp(193.194.195.196)]
#[test]
fn test_ignored() {
    panic!("should be ignored")
}

用户/组条件

当用户是特定用户或在特定组时运行集成测试用例。如果默认功能被禁用,则需要 user 功能。

#[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")
}

CPU/内存/交换空间条件

当内存/交换空间足够时运行集成测试用例。如果默认功能被禁用,则需要 resource 功能。

#[test_with::cpu_core(32)]
#[test]
fn test_ignored_by_cpu_core() {
    panic!("should be ignored")
}


#[test_with::phy_core(32)]
#[test]
fn test_ignored_by_physical_cpu_core() {
    panic!("should be ignored")
}

#[test_with::mem(999GB)]
#[test]
fn test_ignored_by_mem() {
    panic!("should be ignored")
}

#[test_with::swap(999GB)]
#[test]
fn test_ignored_by_swap() {
    panic!("should be ignored")
}

可执行文件条件

当可执行文件可访问时运行集成测试用例。如果默认功能被禁用,则需要 executable 功能。

    // `pwd` executable command exists
    #[test_with::executable(pwd)]
    #[test]
    fn test_executable() {
        assert!(true);
    }

    // `/bin/sh` executable exists
    #[test_with::executable(/bin/sh)]
    #[test]
    fn test_executable_with_path() {
        assert!(true);
    }

    // `non` does not exist
    #[test_with::executable(non)]
    #[test]
    fn test_non_existing_executable() {
        panic!("should be ignored")
    }

    // `pwd` and `ls` exist
    #[test_with::executable(pwd, ls)]
    #[test]
    fn test_executables_too() {
        assert!(true);
    }

运行时

我们可以让一个示例执行 cargo 测试运行器所执行的事情,例如:cargo run --example=<example_name>,并在运行时忽略测试用例。示例中的测试用例不再位于 #[cfg(test)]#[test] 中,并使用 #[test_with::runtime_*],测试运行器将把它视为 Rust 的测试,并提供与 cargo test 相同的摘要。

应启用 runtime 功能并将其作为常规依赖项包含,同时也在 Cargo.toml 中包含相应的 libtest-with 功能。

test-with = { version = "0.10", features = ["runtime"] }
libtest-with = { version = "0.6.1-6", features = ["net", "resource", "user", "executable"] }

创建以下运行时宏的示例(test_with::runner!#[test_with::module]#[test_with::runtime_env()])。

test_with::runner!(module_name);

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

或者,您可以使用 runtime_ignore_if 来自定义条件函数。

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);
    }
}

在测试运行器中设置模拟服务的两种方式:一种是通过 struct,另一种是通过 type

test_with::runner!(test_with_mock);

#[test_with::module]
mod test_with_mock {
    pub struct TestEnv {}

    impl Default for TestEnv {
        fn default() -> TestEnv {
            // Set up mock here
            TestEnv {}
        }
    }

    impl Drop for TestEnv {
        fn drop(&mut self) {
            // Tear down mock here
        }
    }
}

或者

test_with::runner!(test_with_mock);

pub struct Moc {}

impl Default for Moc {
    fn default() -> Moc {
        // Set up mock here
        Moc {}
    }
}

impl Drop for Moc {
    fn drop(&mut self) {
        // Tear down mock here
    }
}

#[test_with::module]
mod test_with_mock {
    pub type TestEnv = super::Moc;
}

请查看 example/runner 项目的示例。

锁定

#[test_with::lock(LOCK_NAME)] 通过使用文件锁确保您的测试用例可以一个接一个地运行。第一个参数是文件锁的名称,第二个参数指定等待秒数,默认为60秒。

  // `LOCK` is file based lock to prevent test1 an test2 run at the same time
  #[test_with::lock(LOCK)]
  fn test_1() {
      assert!(true);
  }

  // `LOCK` is file based lock to prevent test1 an test2 run at the same time
  #[test_with::lock(LOCK)]
  fn test_2() {
      assert!(true);
  }

  // `ANOTHER_LOCK` is file based lock to prevent test3 an test4 run at the same time with 3 sec
  // waiting time.
  #[test_with::lock(ANOTHER_LOCK, 3)]
  fn test_3() {
      assert!(true);
  }

  // `ANOTHER_LOCK` is file based lock to prevent test3 an test4 run at the same time with 3 sec
  // waiting time.
  #[test_with::lock(ANOTHER_LOCK, 3)]
  fn test_4() {
      assert!(true);
  }

相关问题

依赖项

~2–35MB
~553K SLoC