Catalyst

Rust 笔记(三)模块和测试

🧪 把测试写在标准里的还是第一次见(可能是我见识太少了)

模块

定义

总之,为了建立工程化的文件,使用 cargo 新建工程。

cargo new <工程名>
cargo new --lib <库名>

工程中的 main.rslib.rs 是所有相关文件的根文件。

利用 mod 定义模块,在模块之中还能再定义模块。

mod my_module{
	mod module_in_module{
		fn add() {}
	}
}

在模块中使用 pub 关键字标明公共方法。结构体和结构体内的字段, pub 是分开的。

主文件以外的文件本身不需要重复定义。如果有文件am.rs,再在am.rs里定义mod am {fn add},那么需要am::am::add才能拿到函数。

路径

  • 调用路径:使用 ::分割。
  • 绝对路径: crate 开头。例 crate::my_module::module_in_module。(需要先在根文件里声明pub mod a;
  • 相对路径: my_module::module_in_module
  • 使用 super 开始父级的相对路径。
  • use 简化后面路径的书写。可以用 as 给予别名。
  • 利用花括号调用同级: use std::{cmp::Ordering, io};
  • 利用 * 导入所有 use std::*;

声明

main.rs 里声明模块才能把文件都连接起来,例:

src/
├── calculate.rs
└── main.rs
//calculate.rs
pub fn add(a: i32, b: i32) -> i32 {
    a + b
}
// main.rs
mod calculate; // 声明模块

fn main() {
    println!("{}", calculate::add(1, 2));
}

拆分

在不同的文件写同一个模块的内容,例:

src/
├── calculate/
│   └── multiply.rs
├── calculate.rs
├── divided.rs
├── minus.rs
└── main.rs
//src\calculate\multiply.rs
pub fn multiply(a: i32, b: i32) -> i32 {
    a * b
}
// src/calculate.rs
pub mod multiply;
// pub mod divided; 不能同级作子级
// to create the module `divided`, create file "src\calculate\divided.rs" or "src\calculate\divided\mod.rs"

pub fn add(a: i32, b: i32) -> i32 {
    a + b
}
// src/divided.rs
pub fn divided(a: i32, b: i32) -> i32 {
    a / b
}
//src/minus.rs
pub mod calculate{
        pub fn minus(a: i32, b: i32) -> i32 {
        a - b
    }
}
mod calculate; // 声明模块
mod minus;

fn main() {
    println!("{}", calculate::add(1, 2));

    // println!("{}", calculate::minus(1, 2));
    println!("{}", minus::calculate::minus(1, 2)); // 无法跨文件共用 mod 括号

    // println!("{}", calculate::multiply(1, 2));
    println!("{}", calculate::multiply::multiply(1, 2));

    // println!("{}", calculate::multiply::divide::divide(1, 2));
}
# Cargo.toml

[lib]
name = "modul"
path = "src/lib.rs"

[[bin]]
name = "modul"
path = "src/main.rs"

测试

基础

在函数前添加 #[test] 表明这是一个测试用的函数。之后运行 cargo test 就可以执行测试函数。

 #[test]
 fn test(){
 }

测试主要分 单元测试集成测试

  • 单元测试 通常是在文件中创建一个名字为 test 的模块包含测试功能,代码在src目录内。

  • 集成测试 则是纯外部代码。通常需要在工程中建立一个 和 src 同级的 test 文件夹。集成测试 只测试 lib

在模块前添加 #[cfg(test)] 表明测试用模块。测试模块只在测试时编译。

#[cfg(test)]
mod tests {
    use super::*; //使用这让测试函数使用外部模块定义的代码

    #[test]
    fn is_true() {
        assert!(true);
    }
}

执行断言:

assert!(<表达式>); //返回成功或失败
assert_eq!(<表达式1>,<表达式2>); //测试两个表达式是否相等

自定义错误信息:

assert!(false,"错误信息 {}", err);

对于应该 panic 的函数在 #[test] 下使用 #[should_panic]

使用 #[ignore] 忽略此测试函数。

控制

cargo test 的参数有作用于 cargo test 本身的,也有作用于生成的测试文件的。如果想要传给生成的测试文件参数,将参数放在分隔符 -- 后:

`cargo test <参数> -- <生成二进制文件的参数>`

例: cargo test -- --test-threads=1

作用于 cargo test 的参数含义
<测试名字>只运行函数名中包含此名字的测试函数/模块
作用于测试文件的参数含义
--test-threads=1测试线程数
--show-output显示测试函数的结果
--ignored只运行被忽略的函数
参考资料