An Camchéachta
An Camchéachta

Reputation: 91

Is there a way of detecting whether code is being called from tests in Rust?

I'd like to be able to check within my code itself whether or not it is running from a test. It would be ideal to do this, for example, to interface with test databases, web services, etc. A simple bool would suffice.

Does Rust have an existing API for this?

Upvotes: 6

Views: 2038

Answers (1)

Shepmaster
Shepmaster

Reputation: 432109

Runtime detection of tests

There's no global variable that is set when running tests. You could add one, but it's pretty complicated to get it right. This example does not get it completely right:

use std::cell::Cell;

thread_local! {
    pub static IS_TESTING: Cell<bool> = Cell::new(false);
}

fn my_code() -> u8 {
    if IS_TESTING.with(|t| t.get()) {
        0
    } else {
        42
    }
}

#[test]
fn one() {
    IS_TESTING.with(|t| t.set(true));

    assert_eq!(0, my_code());

    IS_TESTING.with(|t| t.set(false));
}

To do this correctly, you need to handle a few things:

  1. Tests are run in a multithreaded environment, so you need to make sure that you handle that.
  2. Using a thread-local is probably incorrect if your code spawns new threads, as the variable won't carry over to them.
  3. You need to make sure to set the flag back to false even when the test aborts early due to an assertion failure.

Compile time detection of tests

More likely, what you want is to detect if you are compiling for tests. This is much simpler and you are probably already using the same technique to conditionally compile your tests themselves:

fn my_code() -> u8 {
    if cfg!(test) {
        0
    } else {
        42
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn one() {
        assert_eq!(0, my_code());
    }
}

Dependency injection is better

Editorially, runtime or compile time detection is a bad idea from the perspective of code quality. Instead of littering your code with boatloads of conditional checks that will complicate and perhaps even slow down your code, introduce dependency injection:

trait ValueSource {
    fn value(&self) -> u8;
}

struct ProductionSource;
impl ValueSource for ProductionSource {
    fn value(&self) -> u8 {
        42
    }
}

fn my_code<S>(source: S) -> u8
where
    S: ValueSource,
{
    source.value()
}

#[cfg(test)]
mod test {
    use super::*;
    struct TestSource(u8);
    impl ValueSource for TestSource {
        fn value(&self) -> u8 {
            self.0
        }
    }

    #[test]
    fn one() {
        let src = TestSource(99);
        assert_eq!(99, my_code(src));
    }
}

This will concentrate related details into one object and the compiler monomorphizes the calls, producing optimized code.

Upvotes: 15

Related Questions