测试

Rust将测试视为一流的构造;生态系统中的所有工具都支持测试。编译器提供了一个内置的配置属性,用于指定要测试的模块。还有一个将函数指定为测试的测试属性。当 Cargo 从头开始生成一个项目时,它建立了这个样板文件。让我们来看一个示例项目;我们将其称为阶乘。它将导出一个宏,该宏计算给定整数的阶乘。因为我们以前已经方便地编写了这样一个宏,所以我们将在这里重新使用该代码。请注意,由于此 crate 将用作库,因此它没有 main 函数:

# cargo new factorial --lib 
# cargo test
    Compiling factorial v0.1.0 (file:///Users/Abhishek/Desktop/rustbook/src/ch2/factorial)
     Finished dev [unoptimized + debuginfo] target(s) in 1.6 secs
      Running target/debug/deps/factorial-364286f171614349 
      
running 1 test 
test tests::it_works ... ok 

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

    Doc-tests factorial 
    
running 0 tests 

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

运行 cargo test 运行 Cargo 为我们生成的存根测试。我们将把 factorial 宏的代码复制到 lib.rs ,它们将如下所示:


# #![allow(unused_variables)]
#fn main() {
// chapter2/factorial/src/lib.rs

#[allow(unused_macros)] 
#[macro_export] 
macro_rules! factorial {
    ($x:expr) => { 
        { 
            let mut result = 1; 
            for i in 1..($x+1) { 
                result = result * i; 
            } 
            result 
        }
    };
}

#[cfg(test)] 
mod tests { 
    #[test] 
    fn test_factorial() { 
        assert_eq!(factorial!(5), 120); 
    }     
}
#}

我们还添加了一个测试,以确保 factorial 实际上像我们所期望的那样工作。#[macro_export] 属性告诉编译器这个宏将在 crate 外部使用。编译器内置的 assert_eq! 宏检查两个参数是否确实相等。 我们还需要放置 #[allow(unused_macros)] 属性,因为如果没有它,编译器会抱怨宏未在非测试代码中使用。如果我们再添加一个这样的测试:


# #![allow(unused_variables)]
#fn main() {
#[test] 
fn test_factorial_fail() { 
    assert_eq!(factorial!(5), 121); 
}
#}

这显然是错误的,正如预期的那样,失败并给我们一个描述性的错误。编译器还支持名为 #[should_panic] 的属性,该属性标记应该发生恐慌的测试。 在这种情况下,只有在出现恐慌时才会通过测试。另一种编写测试的方法是在 Cargo 调用中运行的文档中。

这是一个非常重要的工具,在用工作示例记录代码时,可以保证随着代码库的发展而工作。让我们继续为阶乘宏添加一些“文档测试” :

// chapter2/factorial/src/lib.rs

/// The factorial crate provides a macro to compute factorial of a given 
/// integer /// # Examples
///
/// ``` 
/// # #[macro_use] extern crate factorial; 
/// # fn main() { 
/// assert_eq!(factorial!(0), 1); 
/// assert_eq!(factorial!(6), 720);
/// # } 
/// ``` 
///

#[macro_export] macro_rules! factorial {
    ($x:expr) => { 
        { 
            let mut result = 1; 
            for i in 1..($x+1) { 
                result = result * i; 
            } 
            result 
        }
    };
}

宏的 doctests 与其他所有文档的 doctests 有点不同,其方式如下:

  • 他们必须使用 #[macro_use] 属性来标记此处正在使用宏。请注意,依赖于导出宏的 crate 的外部 crate 也必须使用该属性
  • 它们必须定义 main 函数,并在 doctests 中包含一个 extern crate 指令。对于其他一切,编译器根据需要生成主函数。额外的 # 标记将这些标记隐藏在生成的文档中。

一般来说,测试模块、doctests和 #[test] 属性只能用于单元测试。集成测试应该放在顶级测试目录中。

Rust 团队正在努力增加对测试系统中运行基准点的支持。目前只在夜间提供。