Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can we share test utilites between crates?

I have crate-0 that has src/lib.rs as follows:

#[cfg(test)]
pub mod test_utils {
    pub struct OnlyInTests(pub usize);
    pub fn helper() -> usize { 999 }

    #[test]
    fn test_0() { let _ = OnlyInTests(helper()); }
}

I also have crate-1 where I need the testing framework I defined in crate-0:

extern crate crate_0;

#[cfg(test)]
pub mod test_utils {
    // This will error out - cannot find mod test_utils in crate_0
    use crate_0::test_utils::{OnlyInTests, helper()};

    #[test]
    fn test_1() { let _ = OnlyInTests(helper()); }
}

The code here is trivial and can be copy-pasted, but in reality I have complex test utilities which I want to use while testing crate-1.

I cannot separate out the test utilities into a different crate as I would get cyclic dependency error: test_utils would depend on crate-0 create stuff and crate-0 would depend on test_utils for testing). Nor do I actually want to do this as there are further crates down the line whose testing utilities I would like to use in dependent crates.

like image 336
ustulation Avatar asked Jan 17 '17 15:01

ustulation


People also ask

Do Rust tests run in parallel?

Running Tests in Parallel or Consecutively When you run multiple tests, by default they run in parallel using threads, meaning they finish running faster and you get feedback quicker.

What is #[ CFG test )]?

The #[cfg(test)] annotation on the tests module tells Rust to compile and run the test code only when you run cargo test , not when you run cargo build .

How do you test for Rust?

A primary testing method is Salt Spray testing, which as its name indicates, evaluates how resistant a part or material is to corrosion when exposed to a salt spray for extended periods of time. Effective Salt Spray testing must be performed in a controlled environment, such as a closed salt fog tank.


1 Answers

Two solutions with sightly different trade-offs:

Separate Crate for utilities

Put your test utilities but not your tests in a new crate crate-0-testutils. Make crate-0 dev-depend on crate-0-testutils; crate-0-testutils depend on crate-0; crate-1 dev-depend on crate-0-testutils and depend on crate-0.

This does not create a circular dependency as the dev-dependencies do not get propagated. Because of the dev-dependency your tests in crate-0 can still use what is in crate-0-testutils.

This at most doubles the number of your lib crates if all your creates had such utilities.

Use a testing-feature across all crates

Create a feature in the Cargo.toml ofcrate-0 that does not depend on anything:

[features]
testing = []

And one in crate-1 that depends on the above:

testing = ["crate-0/testing"]

Then make the first tests on each of the crates fail when the feature is not enabled, to make for an easier to understand error:

#[cfg(all(not(feature = "testing"),test))]
mod testusage {
    #[test]
    fn panic_without_testing_feature() {
        panic!("Please use the feature testing when running tests.\n\nUse: cargo test --features testing\n\n");
    }
}

Only compile the utilities when the feature is enabled by adding a guard for it:

#[cfg(feature = "testing")]
fn testing_utility() {
}

This only adds one feature in each crate regardless of how many testing features it depends on but has the downside of needing a special invocation of the tests.

Avoid adding this feature in any crate wide dependency declaration. Including the testing-feature from crate-0 in the dev-dependency of crate-1 where crate-1 also has a normal dependency on crate-0 results in cargo building crate-0 with that feature even for the build target of crate-1 even in release mode.

like image 121
Jan Zerebecki Avatar answered Oct 23 '22 23:10

Jan Zerebecki