【问题标题】:How to run setup code before any tests run in Rust?如何在 Rust 中运行任何测试之前运行设置代码?
【发布时间】:2025-12-05 18:55:01
【问题描述】:

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

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

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

【问题讨论】:

    标签: unit-testing 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

      【讨论】:

        最近更新 更多