首页 > 其他分享 >【Rust自学】11.2. 断言(Assert)

【Rust自学】11.2. 断言(Assert)

时间:2025-01-07 11:31:34浏览次数:3  
标签:assert tests smaller larger 11.2 Assert test hold Rust

喜欢的话别忘了点赞、收藏加关注哦(加关注即可阅读全文),对接下来的教程有兴趣的可以关注专栏。谢谢喵!(=・ω・=)
请添加图片描述

11.2.1. 使用assert!宏检查测试结果

assert宏来自标准库,用来确定某个状态是否为true。这个宏可以接收一个返回类型为布尔类型的表达式:

  • assert!内的值为true时测试就会通过,assert!也不会做多余的操作。
  • assert!内的值为falseassert!就会调用panic!,测试失败

看个例子:

#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

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

在存储矩形宽高的结构体Rectangle上声明了方法can_hold来判断矩形能否容下另一个矩形(不考虑斜着放的情况),逻辑很好想,就是看当前矩形的宽高是否都大于另一个矩形就好。

我们该如何测试这个方法呢?由于这个方法的返回类型正好是bool,所以用assert!再好不过:

#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

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

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

    #[test]
    fn larger_can_hold_smaller() {
        let larger = Rectangle {
            width: 8,
            height: 7,
        };
        let smaller = Rectangle {
            width: 5,
            height: 1,
        };

        assert!(larger.can_hold(&smaller));
    }
}

由于test它是一个模块,所以test模块内如果想使用外部的内容就得先导入到当前作用域,这里就是写use super::*;*就是讲外部模块的所有内容都导入进test模块(有关这部分的详细内容可以看 7.2. 路径(Path)Pt.1 7.3. 路径(Path)Pt.2

然后看下面的测试函数,首先声明了两个矩形,一个是larger一个是smaller里面对应存储的就是大的矩形的长宽和小的矩形的宽高,这就是准备(Arrange)数据阶段呢。

下面的assert!宏里调用了方法can_hold,这就是执行(Act)被测试代码的阶段。

最后调用assert!来判断测试是否成功。

这个例子中,larger存储的宽高绝对可以容纳smaller,所以返回一定是true,测试成功。

运行cargo test试一下:

$ cargo test
   Compiling rectangle v0.1.0 (file:///projects/rectangle)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.66s
     Running unittests src/lib.rs (target/debug/deps/rectangle-6584c4561e48942e)

running 1 test
test tests::larger_can_hold_smaller ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests rectangle

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

那如果小的容纳大的呢?

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

    #[test]
    fn larger_can_hold_smaller() {
        //...
    }

    #[test]
    fn smaller_cannot_hold_larger() {
        let larger = Rectangle {
            width: 8,
            height: 7,
        };
        let smaller = Rectangle {
            width: 5,
            height: 1,
        };

        assert!(!smaller.can_hold(&larger));
    }
}

又声明了smaller_cannot_hold_larger这个测试函数,smaller.can_hold(&larger)的返回值一定是false,但前面加了一个取反关键字!,所以最终assert!接收到的仍然是true,测试依然成功:

$ cargo test
   Compiling rectangle v0.1.0 (file:///projects/rectangle)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.66s
     Running unittests src/lib.rs (target/debug/deps/rectangle-6584c4561e48942e)

running 2 tests
test tests::larger_can_hold_smaller ... ok
test tests::smaller_cannot_hold_larger ... ok

test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests rectangle

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

这两个测试都能通过,说明方法can_hold设计的没啥问题。

下面把这个方法改一下,把can_hold宽度比较从>改成<

#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

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

这个代码的逻辑现在就有问题,然后运行一样的测试函数:

$ cargo test
   Compiling rectangle v0.1.0 (file:///projects/rectangle)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.66s
     Running unittests src/lib.rs (target/debug/deps/rectangle-6584c4561e48942e)

running 2 tests
test tests::larger_can_hold_smaller ... FAILED
test tests::smaller_cannot_hold_larger ... ok

failures:

---- tests::larger_can_hold_smaller stdout ----
thread 'tests::larger_can_hold_smaller' panicked at src/lib.rs:28:9:
assertion failed: larger.can_hold(&smaller)
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    tests::larger_can_hold_smaller

test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

error: test failed, to rerun pass `--lib`

有一个测试失败了,说明错误被成功捕获了,这也是编写错误的目的,尽快发现问题。

11.2.2. 使用assert_eq!assert_ne!测试相等性

assert_eq!eq指的是equal,assert_ne!ne指的是not equal。这两者都是来自标准库。

这两个宏都可以传入两个参数,并且可以判断这两个参数是否相等,通常把被测试代码的结果作为一个参数传进去,把所期待的结果作为另外一个参数传进去,然后这两个宏就会判断这两个结果是否相等。

实际上,这两个宏的使用就类似于==!=运算符。但是不同点在于这两个宏如果失败的话就会自动打印出两个参数的值从而帮助开发者观察失败的原因。

使用这两个宏有一定要求,这两个宏使用debug格式来打印参数,所以要求参数实现了PartialEqDebug这两个traits。所有基本类型和大部分标准库类型都实现了,只不过对于自定义的结构体和枚举来说就得自行实现这两个trait。

自行实现Display trait的例子:

use std::fmt;

// 定义一个结构体
struct Point {
    x: i32,
    y: i32,
}

// 为 Point 实现 Display trait
impl fmt::Display for Point {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // 定义格式化的输出
        write!(f, "Point(x: {}, y: {})", self.x, self.y)
    }
}

fn main() {
    let p = Point { x: 10, y: 20 };
    println!("{}", p); // 使用 Display 格式化输出
}

看个使用assert_eq!的例子:

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

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

    #[test]
    fn it_adds_two() {
        let result = add_two(2);
        assert_eq!(result, 4);
    }
}

add_two这个函数就是把参数加2,下面的测试函数it_adds_two调用了add_two,2+2=4,所以期待的add_two(2)的值就是4,把4和函数的调用写进去就可以。其实在Rust中期待的值和函数的调用的位置是可以互换的,有些语言中对位置有明确要求,但Rust确实没有,只是把放在左边(也就是第一个参数)的值叫做左值,另一个叫右值。

输出:

$ cargo test
   Compiling adder v0.1.0 (file:///projects/adder)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.58s
     Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)

running 1 test
test tests::it_adds_two ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests adder

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

下面我们引入一个逻辑错误,把add_twoa + 2改成a + 3,其余不变,看看会发生什么:

pub fn add_two(a: usize) -> usize {
    a + 3
}

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

    #[test]
    fn it_adds_two() {
        let result = add_two(2);
        assert_eq!(result, 4);
    }
}

输出:

$ cargo test
   Compiling adder v0.1.0 (file:///projects/adder)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.61s
     Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)

running 1 test
test tests::it_adds_two ... FAILED

failures:

---- tests::it_adds_two stdout ----
thread 'tests::it_adds_two' panicked at src/lib.rs:12:9:
assertion `left == right` failed
  left: 5
 right: 4
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    tests::it_adds_two

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

error: test failed, to rerun pass `--lib`

失败的时候它把两个值打印出来了,左值是4,右值是5。

现在4和5不相等,那么这时候把测试函数的assert_eq!改成assert_ne!就能够通过测试。

标签:assert,tests,smaller,larger,11.2,Assert,test,hold,Rust
From: https://blog.csdn.net/weixin_71793197/article/details/144954811

相关文章

  • Rust在前端领域有哪些应用?
    Rust在前端领域的应用正逐渐扩展,并以其高性能和安全性受到开发者的关注。以下是一些Rust在前端开发的典型应用:高性能的WebAssembly:Rust可以编译成WebAssembly,这是一种现代Web浏览器支持的二进制代码格式。通过WebAssembly,Rust编写的程序能在浏览器中作为原生代码运行,显著提升......
  • 说说你对Rust的了解?
    关于Rust在前端开发中的应用和了解,我可以从以下几个方面进行阐述:一、Rust语言特性Rust是一种系统编程语言,它提供了内存安全、并发性和高性能等关键特性,这些特性使得Rust在前端开发中也有其独特的应用价值。内存安全:Rust通过所有权系统和借用检查器在编译时捕获许多常见的内存......
  • 你觉得前端开发人员有必要学习Rust吗?
    对于前端开发人员是否有必要学习Rust,这个问题可以从多个角度进行分析。首先,从性能优势的角度来看,Rust确实具有吸引力。作为一种编译型语言,Rust能编译成高效的机器码,提供接近于C/C++的性能。在处理复杂的数据计算或图像处理等需要高性能处理的前端项目中,Rust可以发挥重要作用。此......
  • 你有使用过Rust写过什么应用吗?
    是的,我使用过Rust进行前端开发,具体来说,我主要利用Rust在以下几个方面的优势来构建前端应用:高性能的WebAssembly编译:Rust可以编译成WebAssembly,这是一种在现代Web浏览器中运行的二进制代码格式。通过这种方式,Rust编写的程序能够在浏览器中以原生代码的形式运行,从而极大地提高了......
  • rust学习十五.4、Rc和RefCell指针
    一、前言作为初学者,在只学习了前面几个章节的时候,我以为rust的所有权规则和它说的是一样的。但实际上,rust发明人并没有遵循这个规则。按照我的想法,应该是因为如果坚持那样可能编写某些代码会太痛苦,甚至可能根本无法实现。“可能根本无法实现”这是一个没有去证实的猜想。不过,......
  • RustLoader
    学习rust,练习写一个loader,不足之处还请指教编写隐藏黑框在注释掉所有打印语句后编译运行还是会弹黑框,解决方法是头部添加一行(指定Rust编译器生成的可执行文件为Windows子系统应用程序,而不是控制台应用程序):#![windows_subsystem="windows"]‍反沙箱流速检测......
  • 一文解秘Rust如何与Java互操作
    使用场景JAVA与Rust互操作让Rust可以背靠Java大生态来做更多事情,而Java也可以享受Rust语言特性的内存安全,所有权机制,无畏并发。互操作的典型场景包括:性能优化:利用Rust处理计算密集型任务,提高Java应用的整体性能。系统级编程:结合Rust的底层控制能力与......
  • 11.25
    C/S结构用户界面设计 【实验编号】10003809547j 图形用户界面设计【实验学时】8学时【实验环境】l 所需硬件环境为微机;l 所需软件环境为MicrosoftVisualStudio2013【实验内容】登录代码packagecn.itcast.bookmanager.jframe;importcn.itcast.bookmanage......
  • 11.21
    “AI界拼多多”毋庸置疑,DeepSeek-V3的发布再次证明,开源模型正迅速缩小与封闭模型之间的差距,在多项任务上实现了几乎相当的性能。这对行业发展未尝不是一件好事,不仅降低了某个AI巨头垄断市场的可能性,还为企业提供了更多选择和灵活性。在定价方面,回顾今年5月,DeepSeek发布第......
  • 11.20
    (1)在数学相关基准测试中,DeepSeek-V3在所有非长链式思维(non-long-CoT)的开源和封闭模型中表现最为出色。在特定基准如MATH-500中,它甚至超过了OpenAIo1-preview。(2)在编程相关任务中,DeepSeek-V3在编程竞赛基准(如LiveCodeBench)上表现最佳。对于工程相关任务,尽管DeepSeek-V......