有没有办法检测是否从Rust的测试中调用代码?

时间:2018-06-16 13:11:41

标签: testing rust

我希望能够在我的代码本身内检查它是否在测试中运行。这样做是理想的,例如,与测试数据库,Web服务等接口。一个简单的bool就足够了。

Rust是否有现有的API?

1 个答案:

答案 0 :(得分:6)

运行时检测测试

运行测试时没有设置全局变量。你可以添加一个,但要做到这一点非常复杂。此示例完全正确:

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));
}

要正确执行此操作,您需要处理以下事项:

  1. 测试在多线程环境中运行,因此您需要确保处理它。
  2. 如果您的代码生成 new 线程,则使用线程本地可能不正确,因为该变量不会转移给它们。
  3. 即使由于断言失败而导致测试提前中止,您也需要确保将该标志设置为false。
  4. 编译测试的时间检测

    更有可能的是,您想要检测您是否正在编译进行测试。这更简单,您可能已经使用相同的技术自行有条件地编译测试:

    fn my_code() -> u8 {
        if cfg!(test) {
            0
        } else {
            42
        }
    }
    
    #[cfg(test)]
    mod test {
        use super::*;
    
        #[test]
        fn one() {
            assert_eq!(0, my_code());
        }
    }
    

    依赖注入更好

    从代码质量的角度来看,编辑时,运行时或编译时检测是个坏主意。通过大量的条件检查乱丢您的代码而不会使代码变得复杂甚至可能减慢代码,引入依赖注入

    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));
        }
    }
    

    这会将相关细节集中到一个对象中,编译器将调用单一化,从而生成优化代码。