Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing Rust code by using temp directory/files while avoiding duplicate code

I'm dealing with a lot of tests that all have the same structure as follows:

#[cfg(test)]
mod tests {
    #[test]
    fn test_sample() {
         // create file, optionally write something inside
         File::create(<FILE_NAME>).unwrap();

         // test stuff with file

         // delete file
         fs::remove_file(<FILE_NAME>).unwrap();
    }
}

My main concern is the sheer amount of duplicate code across all tests. I've tried using crates for creating a single global temp directory with the INIT.call_once() pattern shown here, but it doesn't work because I can't declare a static or const variable at the module level without also initializing it. Obviously it would work if I created a separate temp directory in each test, but I'd like to create only one and then inject it inside the tests like rstest does with fixtures.

Is it possible to achieve this kind of behaviour with temp directories? If not, I'd like to try the same thing with temp files, i.e. injecting them as fixtures. It should work, but I wanted to ask just in case. Or perhaps do you have other ideas for approaching this issue?

like image 697
rdxdkr Avatar asked Aug 25 '26 19:08

rdxdkr


1 Answers

I've done something similar but with more files to be created, although it could get kinda messy but it's the best solution in my case.

It's basically using a wrapper function so that the test functions can get the path to the files/dirs created before running the actural tests:

//tests/common.rs
pub fn run<T>(test: T, clean_after: bool)
where
    T: FnOnce(&TestPaths) -> () + panic::UnwindSafe,
{
    let paths = init().unwrap();
    let res = panic::catch_unwind(|| {
        test(&paths);
    });
    if clean_after {
        clean(&paths).unwrap();
    }
    assert!(res.is_ok());
}

and in each test functions:

#[test]
fn tast1() {
    run(
        |paths| {
            // package metadata
            let p = paths.root.join("test1.toml");
            ...
        },
        false,
    );
}

And you'll have to write your own init() function to return the what ever paths created, and clear(&paths) to clears them ofc.

like image 188
Jonathan Lee Avatar answered Aug 27 '26 15:08

Jonathan Lee