当前位置: 首页 > news >正文

趣味学RUST基础篇(测试)

代码测试是啥?

如果把程序比作一个生命体,就可以把代码测试想象成给程序做“体检”。我们写程序的时候,当然希望它按照我们想的去工作,但这个保证起来其实挺难的。

Rust语言本身已经帮我们避免了很多常见错误(比如用错数据类型或者内存管理出问题),但它也没法知道我们写的逻辑对不对。比如说,我们写了个给数字加2的函数,Rust能确保这个函数只能接收数字,但它没法知道这个函数是不是真的加了2,而不是误写成加10或减50。

这时候测试就派上用场了!我们可以专门写一些测试代码,来验证我们的主代码是否正常工作。比如针对那个加2的函数,我们可以写个测试:“如果我输入3,结果应该是5”。以后每次改代码,都可以跑一下这些测试,确保没有不小心把原来对的功能改错了。

测试本身是门大学问,这章我们不会面面俱到,主要看看Rust提供的测试工具怎么用:包括写测试的语法、运行测试的方法,以及怎么合理组织测试代码。

当然可以!下面是对 Rust 书籍第十一章第一节 “编写测试”趣味化、通俗易懂的转写版本,保留原意的同时,让它更像朋友之间的聊天,轻松易懂,适合初学者阅读。

Rust 测试入门:给你的代码做个体检!

想象一下,你写了一个函数,它负责把两个数加起来。你信心满满地运行程序,结果用户输入 2 + 2,系统却返回了 5……
这不就完蛋了吗?

为了避免这种“数学老师当场去世”的尴尬场面,我们就要给代码做“体检”——这就是**测试(Testing)**的作用。

在 Rust 中,测试就像是一位尽职尽责的质检员,专门检查你写的代码有没有“生病”。它通常会做三件事:

  1. 准备道具:比如先造两个数字、一个对象。
  2. 执行代码:让被测试的函数跑一跑。
  3. 检查结果:看看输出是不是你想要的,不是就“报警”!

Rust 给我们准备了一套“测试工具箱”,里面有:

  • #[test]:标记哪个函数是“质检员”。
  • assert!assert_eq!:断言工具,相当于“必须满足这个条件”。
  • should_panic!:专门检查“该崩溃的时候是不是真崩溃了”。

下面我们一个个来认识这些“工具”。


第一步:让函数变成“测试员”

怎么让一个普通函数变成“测试函数”?很简单,加一行注解:

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

只要加上 #[test],Rust 就知道:“哦,这个函数是用来测试的,等会儿要运行它。”

你用 cargo new my_crate --lib 创建项目时,Rust 会自动给你生成一个测试模板:

#[cfg(test)]
mod tests {#[test]fn it_works() {let result = 2 + 2;assert_eq!(result, 4);}
}

别被 #[cfg(test)]mod tests 吓到,它们的意思是:

  • mod tests:建一个叫“tests”的小房间,专门放测试代码。
  • #[cfg(test)]:这个房间只在运行测试时才存在,发布代码时自动消失(省空间)。

跑测试:cargo test

在终端输入:

$ cargo test

你会看到类似这样的输出:

running 1 test
test tests::it_works ... oktest result: ok. 1 passed; 0 failed

恭喜!你的“2+2=4”测试通过了!

如果测试失败了呢?比如你故意写成:

#[test]
fn another() {panic!("我就是要失败!");
}

再运行 cargo test,就会看到:

test tests::another ... FAILED
thread 'tests::another' panicked at '我就是要失败!'

Rust 会清楚告诉你:哪个测试失败了,为什么失败,甚至在第几行出的问题。简直比老师批作业还仔细!


断言:用 assert! 确保条件成立

有时候我们不想比数字,而是想检查某个条件是否成立。比如:

“这个矩形能装下另一个矩形吗?”

我们可以写个 can_hold 方法:

struct Rectangle {width: u32,height: u32,
}impl Rectangle {fn can_hold(&self, other: &Rectangle) -> bool {self.width > other.width && self.height > other.height}
}

然后写个测试:

#[test]
fn larger_can_hold_smaller() {let big = Rectangle { width: 8, height: 7 };let small = Rectangle { width: 5, height: 1 };assert!(big.can_hold(&small)); // 断言:大的能装下小的
}
  • assert!(条件):如果条件为 true,测试通过;为 false,测试失败并 panic。
  • 如果我们把 > 错写成 <,测试就会失败,Rust 会提醒你:“兄弟,你代码有 bug!”

比较相等?用 assert_eq!assert_ne!

最常用的测试方式就是:看看函数输出是不是你预期的值

比如你写了个 add_two 函数:

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

测试它:

#[test]
fn it_adds_two() {assert_eq!(4, add_two(2)); // 期望:add_two(2) == 4
}
  • assert_eq!(期望值, 实际值):两个值相等就通过。
  • assert_ne!(a, b):两个值不相等才通过(比如你希望输出不是 0)。

如果测试失败,Rust 会贴心地告诉你:

assertion `left == right` failedleft: 4right: 5

这比只说“断言失败”有用多了,一眼就知道是 4 != 5,bug 很好定位。

提示:自定义结构体要实现 #[derive(PartialEq, Debug)] 才能用 assert_eq!


给错误加点“人情味”:自定义错误消息

默认的错误信息有时候太冷冰冰了。比如:

assert!(result.contains("Carol"));

失败时只说:“断言失败”,你根本不知道 result 到底是啥。

解决办法:加自定义消息!

assert!(result.contains("Carol"),"Greeting did not contain name, value was `{}`",result
);

这样失败时就会输出:

Greeting did not contain name, value was `Hello!`

是不是清楚多了?就像朋友在告诉你:“兄弟,你忘了把名字加进去啊!”


测试“崩溃”?用 should_panic

有些代码,我们希望它在出错时崩溃。比如一个“猜数字”游戏,只允许 1~100 的数字:

pub struct Guess {value: i32,
}impl Guess {pub fn new(value: i32) -> Guess {if value < 1 || value > 100 {panic!("必须是 1~100 的数,你给的是 {}!", value);}Guess { value }}
}

我们想测试:如果输入 200,它会不会 panic?

可以这样写:

#[test]
#[should_panic]
fn too_big() {Guess::new(200);
}
  • #[should_panic]:表示“这个测试,只有代码 panic 了才算成功!”
  • 如果 Guess::new(200) 没有 panic,测试反而失败。

更进一步,你还可以检查 panic 的错误信息是否包含某个关键词:

#[test]
#[should_panic(expected = "less than or equal to 100")]
fn too_big() {Guess::new(200);
}

这样就确保了 panic 的原因是对的,而不是因为别的 bug 导致的崩溃。


更优雅的失败方式:用 Result<T, E>

前面的测试,失败就直接 panic,有点“暴力”。

其实你也可以让测试返回 Result,更温和地处理错误:

#[test]
fn it_works() -> Result<(), String> {if 2 + 2 == 4 {Ok(())} else {Err("2 + 2 居然不等于 4!".to_string())}
}

好处是:你可以在测试里用 ? 操作符,处理可能出错的操作。

注意:用了 Result 就不能再用 #[should_panic] 了。想检查是否返回 Err,可以用 assert!(result.is_err())


总结:测试的“黄金三原则”

  1. 准备 → 执行 → 断言:测试三步走,稳如老狗。
  2. 多用 assert_eq!assert_ne!:清晰明了,失败时还能看到具体值。
  3. 善用自定义消息和 should_panic:让错误信息更有“人情味”,定位 bug 更快。
http://www.xdnf.cn/news/1453213.html

相关文章:

  • Qt Creator 打包应用程序时经常会遇到各种问题
  • 【leetcode】64. 最小路径和
  • C++11 类功能与包装器
  • 基于Taro4打造的一款最新版微信小程序、H5的多端开发简单模板
  • 盲盒抽卡机小程序系统开发:以技术创新驱动娱乐体验升级
  • 算法训练营DAY58 第十一章:图论part08
  • elasticsearch-7.17.29 集群案例
  • helm 的常用命令
  • Spring Cloud Eureka 核心原理
  • React 中的 HOC 和 Hooks
  • Java Web 是技术与产业的 “交叉赋能点”
  • 原生住宅IP有多顶?跨境圈都在用
  • MaxKB4j智能体平台 Docker Compose 快速部署教程
  • webrtc之语音活动上——VAD能量检测原理以及源码详解
  • 桌面应用开发语言与框架选择指南
  • android seekbar显示刻度
  • Python_occ 学习记录 | 细观建模(2)
  • 【C语言】第二课 位运算
  • QT6 配置 Copilot插件
  • Pycharm 试用
  • Spring简单的读取和存储对象
  • 君正T31学习(7)- 启动流程
  • 当有鹿机器人读懂城市呼吸的韵律——具身智能如何重构户外清洁生态
  • 2025变现打法:AI+IP实现高效变现|创客匠人
  • 第十四届蓝桥杯青少组C++国赛[2023.5.28]第二部分编程题(4、 数独填数)
  • JS中正则表达式的运用
  • android Thread线程—HandlerThread
  • 汽车v型推力杆总成三维5自由度性能及疲劳测试系统
  • 追觅科技举办2025「敢梦敢为」发布会,发布超30款全场景重磅新品
  • 【iOS】 懒加载