paul_h
paul_h

Reputation: 2051

How to make a variable with a scope/lifecycle for all test functions in a Rust test?

I have a test that initializes a variable before diving into the detail of the test, and I want to make a second test with the same variable, and not duplicate the initialization code:

#[test]
fn test_one() {
    let root = Path::new("data/");
    // the rest of the test
}
#[test]
fn test_two() {
    let root = Path::new("data/");
    // the rest of the test
}

I don't think static or const would do it because the size would not be known up front, though PathBuf.from(path) might make that OK, except that initialization expressions for static/const vars cannot be too complex.

I've seen lazy_static, but have not seen any examples of its use in tests. This after seeing the compiler error with "an extern crate loading macros must be at the crate root", which online searching tells me is something about being outside main(), but tests don't have main functions.

In Java, I would define the variable then initialize it in a setup() method, but I can't see examples of that online for Rust.

Upvotes: 15

Views: 8526

Answers (1)

Shepmaster
Shepmaster

Reputation: 430663

Foremost, remember that Rust tests are run in parallel. This means that any shared setup needs to be thread-safe.

and not duplicate the initialization code

You do it the same way you avoid duplicating any other code: create a function, create a type, create traits, etc.:

use std::path::PathBuf;

fn root() -> PathBuf {
    PathBuf::from("data/")
}

#[test]
fn test_one() {
    let root = root();
    // the rest of the test
}

#[test]
fn test_two() {
    let root = root();
    // the rest of the test
}

In Java I would define the variable, then initialize it in a setup() method

Instead, make a struct called Setup containing all those variables and construct it as the first thing in each test:

use std::path::{Path, PathBuf};

struct Setup {
    root: PathBuf,
}

impl Setup {
    fn new() -> Self {
        Self {
            root: PathBuf::from("data/"),
        }
    }
}

#[test]
fn test_one() {
    let setup = Setup::new();
    let root: &Path = &setup.root;
    // the rest of the test
}

#[test]
fn test_two() {
    let setup = Setup::new();
    let root: &Path = &setup.root;
    // the rest of the test
}

but have not seen any examples of [lazy-static] use in tests

That's because there is no different way to use it in tests, it's just code:

use lazy_static::lazy_static; // 1.4.0
use std::path::Path;

lazy_static! {
    static ref ROOT: &'static Path = Path::new("data/");
}

#[test]
fn test_one() {
    let root = *ROOT;
    // the rest of the test
}

#[test]
fn test_two() {
    let root = *ROOT;
    // the rest of the test
}

See also:


Very specifically for your case, it's very rare that you need exactly a Path, since a string slice implements AsRef<Path>. Said another way, most places that accept a Path accept a &str:

static ROOT: &str = "data/";

#[test]
fn test_one() {
    let root = ROOT;
    // the rest of the test
}

#[test]
fn test_two() {
    let root = ROOT;
    // the rest of the test
}

Upvotes: 19

Related Questions