## 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](https://crates.io/crates/two-rusty-forks) - a temporary work dir with [tempfile](https://crates.io/crates/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. ```rust #[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 ! ```rust 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`. ```rust #[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. ```rust #[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` ```rust #[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") } ```