A subset of my tests need a resource that is only allowed to be created once. This resource also has to be dropped at some point (not before the last test needing it finished).
Currently I use a static OnceCell:
struct MyResource {}
impl Drop for MyResource {
fn drop(&mut self) {
println!("Dropped MyResource");
}
}
impl MyResource {
fn new() -> MyResource {
println!("Created MyResource");
MyResource {}
}
}
fn main() {
let _my_resource = MyResource::new();
println!("Hello, world!");
}
#[cfg(test)]
mod tests {
use super::*;
use tokio::sync::OnceCell;
static GET_RESOURCE: OnceCell<MyResource> = OnceCell::const_new();
async fn init_my_resource() {
GET_RESOURCE.get_or_init(|| async {
MyResource::new()
}).await;
}
#[tokio::test]
async fn some_test() {
init_my_resource().await;
assert_eq!("some assert", "some assert");
}
}
This works fine except that it never drops the resource.
Is there a better way to do this that also drops the resource?
The answer to How to run setup code before any tests run in Rust? does not answer this question as it only answers how to initialize something once, but not how to drop something after the tests ran.
The most straight-forward solution I found is offered by the crate static_init:
struct MyResource {}
impl Drop for MyResource {
fn drop(&mut self) {
println!("Dropped my rescource");
}
}
impl MyResource {
fn new() -> MyResource {
println!("Created MyResource");
MyResource {}
}
}
fn main() {
let _my_resource = MyResource::new();
println!("Hello, world!");
}
#[cfg(test)]
mod tests {
use super::*;
use static_init::dynamic;
#[dynamic(drop)]
static mut RES: MyResource = MyResource::new();
#[tokio::test]
async fn some_test() {
println!("Running test");
assert_eq!("some assert", "some assert");
}
}
Another proposed solution was using ctor and dtor of the crate ctor. Though using this with a plain static mut RES: MyResource
results in unsafe code as we need to modify a mutable static.