如何在 Rust 中运行任何测试之前运行设置代码?

Posted

技术标签:

【中文标题】如何在 Rust 中运行任何测试之前运行设置代码?【英文标题】:How to run setup code before any tests run in Rust? 【发布时间】:2019-09-19 07:39:04 【问题描述】:

我有一个 Rust 应用程序(一个简单的解释器),需要在环境可用之前进行一些设置(初始化一个 repo)。

我了解 Rust 以多线程方式运行其测试(通过 cargo test),因此我需要在任何测试运行之前初始化 repo。我还需要每次运行只执行一次,而不是在每次测试之前。

在 Java 的 JUnit 中,这将通过 @BeforeClass(或 JUnit 5 中的 @BeforeAll)方法来完成。我怎样才能在 Rust 中实现同样的目标?

【问题讨论】:

【参考方案1】:

没有任何内置功能可以做到这一点,但这应该会有所帮助(您需要在每次测试开始时调用initialize()):

use std::sync::Once;

static INIT: Once = Once::new();

pub fn initialize() 
    INIT.call_once(|| 
        // initialization code here
    );

【讨论】:

【参考方案2】:

如果您使用ctor crate,您可以利用在运行任何测试之前运行的全局构造函数。

这是一个初始化流行的env_logger crate 的示例(假设您已将ctor 添加到Cargo.toml 文件中的[dev-dependencies] 部分):

#[cfg(test)]
#[ctor::ctor]
fn init() 
    env_logger::init();

函数名不重要,你可以随便命名。

【讨论】:

我应该把这个放到每个.rs文件中吗?【参考方案3】:

为了给人们更多的想法(例如,如何不在每次测试中调用setup),您可以做的另一件事是编写这样的帮助程序:

fn run_test<T>(test: T) -> ()
    where T: FnOnce() -> () + panic::UnwindSafe

    setup();    
    let result = panic::catch_unwind(|| 
        test()
    );    
    teardown();    
    assert!(result.is_ok())

然后,在您自己的测试中,您可以这样使用它:

#[test]
fn test() 
    run_test(|| 
        let ret_value = function_under_test();
        assert!(ret_value);
    )

您可以在此处阅读有关UnwindSafe trait 和catch_unwind 的更多信息:https://doc.rust-lang.org/std/panic/fn.catch_unwind.html

我在 Eric Opines 的 this medium article 中找到了这个测试助手的最初想法。

此外,rstest crate 具有 pytest-like 固定装置,您可以将其用作设置代码(与 Jussi Kukkonen's answer 结合使用:

use std::sync::Once; 
use rstest::rstest;
static INIT: Once = Once::new();

pub fn setup() -> ()  
    INIT.call_once(|| 
        // initialization code here
    );


#[rstest]
fn should_success(setup: ()) 
    // do your test

也许有一天 rstest 将获得范围支持,并且不再需要 Once

【讨论】:

以上是关于如何在 Rust 中运行任何测试之前运行设置代码?的主要内容,如果未能解决你的问题,请参考以下文章

Rust 和 Wasm 初始设置

如何在 Python 中的每个单元测试之前和之后运行特定代码

如何在测试失败后但在任何 @After 方法之前让 JUnit 4.8 运行代码?

Rust编程语言入门之编写自动化测试

XCode OCUnit 在运行任何测试代码之前崩溃并出现 Abort 陷阱:6

在 py.test 中的每个测试之前和之后运行代码?