RustPrimer
  • Introduction
  • 初识Rust
  • 安装Rust
    • Linux
    • Mac
    • Windows
    • 版本管理工具: rustup
  • 编辑器
    • 前期准备
    • vim
    • emacs
    • vscode
    • atom
    • sublime
    • visual studio
    • spacemacs
  • Rust快速入门
    • Rust旅程
    • 变量绑定与原生类型
    • 数组、动态数组和字符串
    • 结构体与枚举
    • 控制流
    • 函数与方法
    • 特性
    • 注释与文档
    • 输入输出流
  • Cargo项目管理器
  • 基本程序结构
    • 注释
    • 条件
    • 循环
  • 类型、运算符和字符串
    • 基础类型
    • 复合类型
    • 字符串类
    • 基础运算符和字符串格式化
  • 函数
    • 函数参数
    • 函数返回值
    • 语句和表达式
    • 高阶函数
  • 模式匹配
    • match关键字
    • 模式 pattern
  • 特征 Trait
    • trait关键字
    • trait对象
  • 泛型
  • 可变性、所有权、租借和生命期
    • 所有权
    • 引用和借用
    • 生命周期
  • 闭包
    • 闭包的语法
    • 闭包的实现
    • 闭包作为参数和返回值
  • 集合类型 Collections
    • 动态数组 Vec
    • 哈希表 HashMap
  • 迭代器
    • 迭代器、适配器、消费者
  • 模块和包系统、Prelude
    • 模块 module 和包 crate
    • Prelude
  • Option、Result与错误处理
  • 输入与输出
  • 宏系统
  • 堆、栈与Box
  • 几种智能指针
    • Rc, Arc
    • Mutex, RwLock
    • Cell, RefCell
  • 类型系统中的几个常见 Trait
    • Into/From 及其在 String 和 &str 互转上的应用
    • AsRef, AsMut
    • Borrow, BorrowMut, ToOwned
    • Deref 与 Deref coercions
    • Cow 及其在 String 和 &str 上的应用
  • Send 和 Sync
  • 并发,并行,多线程编程
    • 线程
    • 消息传递
    • 共享内存
    • 同步
    • 并行
  • Unsafe、原始指针
    • Unsafe
    • 原始指针
  • FFI
    • rust调用ffi函数
    • 将rust编译成库
  • 运算符重载
  • 属性和编译器参数
    • 属性
    • 编译器参数
  • Cargo参数配置
  • 测试与评测
    • 测试 (testing)
    • 评测 (benchmark)
  • 代码风格
  • Any与反射
  • 安全(safe)
  • 常用数据结构实现
    • 栈结构
    • 队列
    • 二叉树
    • 优先队列
    • 链表
    • 图结构
  • 标准库介绍
    • 系统命令:调用grep
    • 目录操作:简单grep
    • 网络模块:W回音
  • 实战篇
    • 实战:Json处理
    • 实战:Web 应用开发入门
    • 实战:使用Postgresql数据库
  • 附录-术语表
Powered by GitBook
On this page
  • 测试
  • 函数级测试
  • 模块级测试
  • 工程级测试
  • 文档级测试
  • [cfg(test)]
  • [test]
  • 结语

Was this helpful?

  1. 测试与评测

测试 (testing)

测试

程序测试是一种找到缺陷的有效方式,但是它对证明没有缺陷却无能为力。

Edsger W. Dijkstra, "The Humble Programmer" (1972)

作为软件工程质量保障体系的重要一环,测试是应该引起我们充分注意并重视的事情。前面说过,Rust 语言的设计集成了最近十多年中总结出来的大量最佳工程实践,而对测试的原生集成也正体现了这一点。下面来看 Rust 是怎么设计测试特性的。

Rust 的测试特性按精细度划分,分为 3 个层次:

  1. 函数级;

  2. 模块级;

  3. 工程级;

另外,Rust 还支持对文档进行测试。

函数级测试

在本章中,我们用创建一个库的实操来讲解测试的内容。我们先用 cargo 建立一个库工程:adder

$ cargo new adder
$ cd adder

#[test] 标识

打开 src/lib,rs 文件,可以看到如下代码

#[test]
fn it_works() {
    // do test work
}

Rust 中,只需要在一个函数的上面,加上 #[test] 就标明这是一个测试用的函数。

有了这个属性之后,在使用 cargo build 编译时,就会忽略这些函数。使用 cargo test 可以运行这些函数。类似于如下效果:

$ cargo test
   Compiling adder v0.0.1 (file:///home/you/projects/adder)
     Running target/adder-91b3e234d4ed382a

running 1 test
test it_works ... ok

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

   Doc-tests adder

running 0 tests

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

Rust 提供了两个宏来执行测试断言:

assert!(expr)               测试表达式是否为 true 或 false
assert_eq!(expr, expr)      测试两个表达式的结果是否相等

比如

#[test]
fn it_works() {
    assert!(false);
}

运行 cargo test,你会得到类似下面这样的提示

$ cargo test
   Compiling adder v0.0.1 (file:///home/you/projects/adder)
     Running target/adder-91b3e234d4ed382a

running 1 test
test it_works ... FAILED

failures:

---- it_works stdout ----
        thread 'it_works' panicked at 'assertion failed: false', /home/steve/tmp/adder/src/lib.rs:3



failures:
    it_works

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured

thread '<main>' panicked at 'Some tests failed', /home/steve/src/rust/src/libtest/lib.rs:247

#[should_panic] 标识

如果你的测试函数没完成,或没有更新,或是故意让它崩溃,但为了让测试能够顺利完成,我们主动可以给测试函数加上 #[should_panic] 标识,就不会让 cargo test 报错了。

如

#[test]
#[should_panic]
fn it_works() {
    assert!(false);
}

运行 cargo test,结果类似如下:

$ cargo test
   Compiling adder v0.0.1 (file:///home/you/projects/adder)
     Running target/adder-91b3e234d4ed382a

running 1 test
test it_works ... ok

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

   Doc-tests adder

running 0 tests

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

#[ignore] 标识

有时候,某个测试函数非常耗时,或暂时没更新,我们想不让它参与测试,但是又不想删除它,这时, #[ignore] 就派上用场了。

#[test]
#[ignore]
fn expensive_test() {
    // code that takes an hour to run
}

写上这个,运行 cargo test 的时候,就不会测试这个函数。

模块级测试

有时,我们会组织一批测试用例,这时,模块化的组织结构就有助于建立结构性的测试体系。Rust 中,可以类似如下写法:

pub fn add_two(a: i32) -> i32 {
    a + 2
}

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

    #[test]
    fn it_works() {
        assert_eq!(4, add_two(2));
    }
}

也即在 mod 的上面写上 #[cfg(test)] ,表明这个模块是个测试模块。一个测试模块中,可以包含若干测试函数,测试模块中还可以继续包含测试模块,即模块的嵌套。

如此,就形式了结构化的测试体系,甚是方便。

工程级测试

函数级和模块级的测试,代码是与要测试的模块(编译单元)写在相同的文件中,一般做的是白盒测试。工程级的测试,一般做的就是黑盒集成测试了。

我们看一个工程的目录,在这个目录下,有一个 tests 文件夹(没有的话,就手动建立)

Cargo.toml
Cargo.lock
examples
src
tests

我们在 tests 目录下,建立一个文件 testit.rs ,名字随便取皆可。内容为:

extern crate adder;

#[test]
fn it_works() {
    assert_eq!(4, adder::add_two(2));
}

这里,比如,我们 src 中,写了一个库,提供了一个 add_two 函数,现在进行集成测试。

首先,用 extern crate 的方式,引入这个库,由于是同一个项目,cargo 会自动找。引入后,就按模块的使用方法调用就行了,其它的测试标识与前面相同。

写完后,运行一下 cargo test,提示类似如下:

$ cargo test
   Compiling adder v0.0.1 (file:///home/you/projects/adder)
     Running target/adder-91b3e234d4ed382a

running 1 test
test tests::it_works ... ok

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

     Running target/lib-c18e7d3494509e74

running 1 test
test it_works ... ok

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

   Doc-tests adder

running 0 tests

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

文档级测试

Rust 对文档的哲学,是不要单独写文档,一是代码本身是文档,二是代码的注释就是文档。Rust 不但可以自动抽取代码中的文档,形成标准形式的文档集合,还可以对文档中的示例代码进行测试。

比如,我们给上面库加点文档:

//! The `adder` crate provides functions that add numbers to other numbers.
//!
//! # Examples
//!
//!

//! assert_eq!(4, adder::add_two(2)); //! ```

/// This function adds two to its argument. /// /// # Examples /// /// /// use adder::add_two; /// /// assert_eq!(4, add_two(2)); ///

pub fn add_two(a: i32) -> i32 { a + 2 }

[cfg(test)]

mod tests { use super::*;

[test]

fn it_works() { assert_eq!(4, add_two(2)); } }

运行 `cargo test`,结果如下:

$ cargo test Compiling adder v0.0.1 (file:///home/steve/tmp/adder) Running target/adder-91b3e234d4ed382a

running 1 test test tests::it_works ... ok

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

 Running target/lib-c18e7d3494509e74

running 1 test test it_works ... ok

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

Doc-tests adder

running 2 tests test add_two_0 ... ok test _0 ... ok

test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured

```

看到了吧,多了些测试结果。

结语

我们可以看到,Rust 对测试,对文档,对文档中的示例代码测试,都有特性支持。从这些细节之处,可以看出 Rust 设计的周密性和严谨性。

但是,光有好工具是不够的,工程的质量更重要的是写代码的人决定的。我们应该在 Rust 严谨之风的熏陶下,养成良好的编码和编写测试的习惯,掌握一定的分析方法,把质量要求贯彻到底。

Previous测试与评测Next评测 (benchmark)

Last updated 5 years ago

Was this helpful?