Rust 里没有 JUnit 那种“全局 before/after”钩子,但setup/teardown 通常有三层做法:简单场景用Drop,测试上下文用test-context,复杂套件用rstestfixture 或suitest。
1. 最轻量:用Drop做 teardown
Rust 的Drop天然适合做清理:连接池、临时文件、测试目录、临时端口绑定都可以靠它回收。
struct TempDir { path: std::path::PathBuf, } impl TempDir { fn new() -> Self { let path = std::env::temp_dir().join("my_test"); std::fs::create_dir_all(&path).unwrap(); Self { path } } } impl Drop for TempDir { fn drop(&mut self) { let _ = std::fs::remove_dir_all(&self.path); } } #[test] fn test_with_temp_dir() { let dir = TempDir::new(); // 使用 dir.path 做测试 }2. 最接近 JUnit:test-context
如果你想要明确的setup()/teardown(),test-context很合适,还能配合#[tokio::test]。
use test_context::{test_context, AsyncTestContext}; struct AppContext { db_url: String, } #[async_trait::async_trait] impl AsyncTestContext for AppContext { async fn setup() -> Self { Self { db_url: "postgres://localhost:5432/test".into(), } } async fn teardown(self) { // 清理测试数据库、关闭连接池等 } } #[test_context(AppContext)] #[tokio::test] async fn test_handler(ctx: &mut AppContext) { assert!(!ctx.db_url.is_empty()); }3. 参数化 + fixture:rstest
rstest没有显式的beforeEach/afterEach,但可以用 fixture 做 setup,用Drop或#[once]控制生命周期。
use rstest::*; #[fixture] fn db_url() -> String { "postgres://localhost/test".into() } #[fixture] fn temp_dir() -> TempDir { TempDir::new() } #[rstest] fn test_query(db_url: String, temp_dir: TempDir) { assert!(!db_url.is_empty()); }如果 fixture 需要全局共享,比如测试数据库连接池,可以用:
#[fixture] #[once] fn pool() -> Pool { Pool::new() }4. 需要完整套件钩子:suitest/spectacular
如果你确实需要before_all / before_each / after_each / after_all,可以用suitest。
use suitest::{suite, before_all, after_all, before_each, after_each}; #[suite(my_suite)] mod tests { use suitest::*; #[before_all] async fn setup() -> usize { 42 } #[after_all] async fn teardown(value: usize) { assert_eq!(value, 42); } #[before_each] fn before() -> String { "ready".into() } #[after_each] fn after(state: String) { assert_eq!(state, "ready"); } #[test] fn it_works(state: String, value: usize) { assert_eq!(state, "ready"); assert_eq!(value, 42); } }选型建议
| 场景 | 推荐 |
|---|---|
| 临时文件、临时目录、简单资源清理 | Drop |
| 每个测试需要统一上下文 | test-context |
| 参数化测试 + fixture 注入 | rstest |
| 需要 before/after 套件钩子 | suitest/spectacular |
| 框架集成测试 | rstest+Drop+mockall |
如果是在搭后端框架的测试基础设施,建议优先用rstest管理 fixture,Drop管 teardown,test-context补显式 setup/teardown。这样既能保持工程化边界清晰,也不会引入太重的测试 harness。
需要的话,我可以帮你把这套封装成框架里的TestApp和TestFixture基类。