#testing #proc-macro-attributes #env-var #temp-dir #environment #isolated #run-test

dev sealed_test

A procmacro attribute to run your test in an isolated environment

4 releases (2 stable)

1.1.0 Jun 5, 2024
1.0.0 Apr 9, 2022
0.2.0 Feb 5, 2022
0.1.0 Dec 7, 2021

#77 in Filesystem

Download history 3570/week @ 2024-08-12 3975/week @ 2024-08-19 3821/week @ 2024-08-26 3799/week @ 2024-09-02 5002/week @ 2024-09-09 5284/week @ 2024-09-16 4019/week @ 2024-09-23 4545/week @ 2024-09-30 4725/week @ 2024-10-07 4768/week @ 2024-10-14 4827/week @ 2024-10-21 3600/week @ 2024-10-28 4730/week @ 2024-11-04 4303/week @ 2024-11-11 4915/week @ 2024-11-18 4105/week @ 2024-11-25

18,180 downloads per month
Used in 15 crates

MIT license

15KB
145 lines

Sealed test

This crate exposes the #[sealed_test] macro attribute to run your tests in an isolated environment. It provides the following :

  • an isolated process using rusty-fork
  • a temporary work dir with tempfile.
  • a set of handy attributes to configure your tests including:
    • before/after: setup and teardown functions for your tests.
    • env: set environment variables in the test process.
    • files: copy files from your crate directory to the test temporary directory.

Caution: using #[sealed_test] instead of #[test] will create a temporary file and set it to be the test current directory but, nothing stops you from changing that directory using std::env::set_current_dir.

Why ?

If you run cargo test your tests will run in parallel, in some case this could be problematic. Let us examine a concrete example.

 #[test]
fn foo() -> Result<(), VarError> {
    std::env::set_var("VAR", "foo");
    let var = std::env::var("VAR")?;
    assert_eq!(var, "foo");
    Ok(())
}

#[test]
fn bar() -> Result<(), VarError> {
    std::env::set_var("VAR", "bar");
    // During the thread sleep, the `foo` test will run
    // and set the environment variable to "foo"
    std::thread::sleep(Duration::from_secs(1));
    let var = std::env::var("VAR")?;
    // If running tests on multiple threads the below assertion will fail
    assert_eq!(var, "bar");
    Ok(())
}

With sealed_test

Here each test has its own environment, the tests will always pass !

use sealed_test::prelude::*;

#[sealed_test]
fn foo() -> Result<(), VarError> {
   std::env::set_var("VAR", "bar");
    let var = std::env::var("VAR")?;
    assert_eq!(var, "bar");
    Ok(())
}

#[sealed_test]
fn bar() -> Result<(), VarError> {
    std::env::set_var("VAR", "bar");
    std::thread::sleep(Duration::from_secs(1));
    let var = std::env::var("VAR")?;
    assert_eq!(var, "bar");
    Ok(())
}

Examples

The env attribute

The env attribute allow to quickly set environment variable in your tests. This is only syntactic sugar and you can still normally manipulate environment variables with std::env.

#[sealed_test(env = [ ("FOO", "foo"), ("BAR", "bar") ])]
fn should_set_env() {
    let foo = std::env::var("FOO").expect("Failed to get $FOO");
    let bar = std::env::var("BAR").expect("Failed to get $BAR");
    assert_eq!(foo, "foo");
    assert_eq!(bar, "bar");
}

Tip: Sealed tests have their own environment and variable from the parent won't be affected by whatever you do with the test environment.

The files attribute

If you need test behaviors that mutate the file system, you can use the files attribute to quickly copy files from your crate root to the test working directory. The test working directory lives in tmpfs and will be cleaned up after the test execution.

#[sealed_test(files = ["tests/foo", "tests/bar"])]
fn should_copy_files() {
    assert!(PathBuf::from("foo").exists());
    assert!(PathBuf::from("bar").exists());
}

Setup and teardown

Use before and after to run a rust expression around your tests, typically a function, for instance setup = setup_function().

before and after

#[sealed_test(before = setup(), after = teardown())]
fn should_run_setup_and_tear_down() {
    // ...
}

fn setup() {
    println!("Hello from setup")
}

fn teardown() {
    println!("Hello from teardown")
}

Dependencies

~2–11MB
~134K SLoC