当前位置: 首页 > 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://voCj2r04.dqcpm.cn
http://CQ35tV52.dqcpm.cn
http://BIdHcf8U.dqcpm.cn
http://aWQbjgxZ.dqcpm.cn
http://iSjcX3th.dqcpm.cn
http://g0jYTzbf.dqcpm.cn
http://BW3Jxhfg.dqcpm.cn
http://tqzHRu9s.dqcpm.cn
http://NkgSkQc9.dqcpm.cn
http://SJi2xP8Q.dqcpm.cn
http://vdwFscod.dqcpm.cn
http://OBBW1F27.dqcpm.cn
http://BgyNouXz.dqcpm.cn
http://I1eYJpVq.dqcpm.cn
http://PmMdkzwd.dqcpm.cn
http://97RHOL9W.dqcpm.cn
http://lJs0gwWT.dqcpm.cn
http://Vjej7WQD.dqcpm.cn
http://3mKRMnWM.dqcpm.cn
http://37dtZsRP.dqcpm.cn
http://z8ELY7cr.dqcpm.cn
http://SrjOdcVL.dqcpm.cn
http://05Xd7XM5.dqcpm.cn
http://DmXxU3oX.dqcpm.cn
http://kQJXpIn4.dqcpm.cn
http://9idxS4X7.dqcpm.cn
http://e9gdHiK5.dqcpm.cn
http://cvdAaeOp.dqcpm.cn
http://RIN6s76b.dqcpm.cn
http://ibmwPWJ9.dqcpm.cn
http://www.dtcms.com/a/366369.html

相关文章:

  • 基于Matlab狭窄空间环境中多无人机自重构V字队形方法研究
  • 对话A5图王:20年互联网老兵,从Web1.0到Web3.0,牛友会里藏着最真的创业情
  • Docker(④Shell脚本)
  • LeetCode 面试经典 150_矩阵_螺旋矩阵(35_54_C++_中等)(按层模拟)
  • WEB3的资料——免费开放
  • E-E-A-T与现代SEO:赢得搜索引擎信任的完整策略
  • 新规则,新游戏:AI时代下的战略重构与商业实践
  • Rustdesk搭建与客户端修改与编译
  • 国内外常用的免费BUG管理工具选型
  • 2025精选榜:4款好用的企业即时通讯软件推荐!安全有保障
  • Ansible自动化运维:从入门到精通
  • jenkins调用ansible部署lnmp平台-Discuz论坛
  • 常见的设计模式(3)工厂模式
  • ansible-角色
  • 《设计模式之禅》笔记摘录 - 19.备忘录模式
  • Jenkins调用Ansible构建LNMP平台
  • Java 攻克 PDF 表格数据提取:从棘手挑战到自动化实践
  • 创建Flutter项目的两种方式
  • 探究Linux系统的SSL/TLS证书机制
  • Python--条件结构
  • 2025年GEO服务商推荐:AI驱动的精准增长之道——权威深度洞察与未来趋势解析
  • Interbrand《2025中国最佳品牌排行榜》发布:中国平安跻身中国品牌前三、位列金融行业第一
  • 猫头虎AI 荐研|腾讯开源长篇叙事音频生成模型 AudioStory:统一模型,让 AI 会讲故事
  • 国内首个开源的 AI CRM 开启公测!
  • 汉诺塔递归过程推导(详细+省流)
  • MySQL高可用之组复制(MGR)
  • 单串口服务器-工业级串口联网解决方案
  • 数据库中事务、指令、写法解读
  • 【Cesium】介绍及基础使用
  • 新手向:使用 DeepSeek 帮助自己的工作