Rust学习第八天——编写和运行测试

测试(函数)

  • 测试:

  • 函数

  • 验证非测试代码功能是否和预期一致

  • 测试函数体(通常)执行的3个操作:

  • 准备数据/状态

  • 运行被测试的代码

  • 断言(Assert)结果

解剖测试函数

  • 测试函数需要使用test属性(attribute)进行标注

  • Attribute就是一段Rust代码的元数据

  • 在函数上加#[test],可把函数变成测试函数

运行测试

  • 使用cargo test命令运行所有测试函数

  • Rust会构建一个Test Runner可执行文件

  • 当使用cargo创建library项目的时候,会生成一个test module,里面有一个test 函数

  • 你可以添加任意数量的test module或函数

cargo new adder --lib
cargo test

测试失败

  • 测试函数panic就表示失败

  • 每个测试运行在一个新线程

  • 当主线程看见某个测试线程挂掉了,那个测试标记为失败了

pub fn add(left: usize, right: usize) -> usize {
    left + right
}

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

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

    #[test]
    fn another() {
        panic!("Make this test fail");
    }
}

断言(Assert)

  • 使用assert!宏检查测试结果

  • true: 测试通过

  • false:调用panic,测试失败

pub fn add(left: usize, right: usize) -> usize {
    left + right
}

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

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

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

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

    #[test]
    fn larger_can_hold_smaller() {
        let larger = Rectangle {
            length: 8,
            width: 7,
        };
        let smaller = Rectangle {
            length: 5,
            width: 1,
        };
        assert!(larger.can_hold(&smaller));
    }

    #[test]
    fn smaller_can_hold_smaller() {
        let larger = Rectangle {
            length: 8,
            width: 7,
        };
        let smaller = Rectangle {
            length: 5,
            width: 1,
        };
        assert!(!smaller.can_hold(&larger));
    }
}

使用assert_eq!和assert_ne!测试相等性

  • 都来自标准库

  • 判断两个参数是否相等或不等

  • 实际上,他们使用的就是==和!=元素符

  • 断言失败:自动打印出两个参数的值

  • 使用debug格式打印参数

  • 要求参数实现了PartialEq和Debug Trait(所有的基本类型和标准库里大部分类型都实现了)

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

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn it_adds_two() {
        assert_eq!(4, add_two(2));
    }
}

添加自定义错误消息

  • 可以向assert!、assert_eq!、assert_ne!添加可选的自定义消息

  • 这些自定义消息和失败消息都会打印出来

  • assert!:第1参数必填,自定义消息作为第2个参数

  • assert_eq!和assert_ne!:前两个参数必填,自定义消息作为第3个参数

  • 自定义消息参数会被传递给format!宏,可以使用{}占位符

  • (例子)


pub fn greeting(name: &str) -> String {
    format!("Hello {}!", name)
}

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

    #[test]
    fn greetings_contain_name() {
        let result = greeting("Carol");
        assert!(result.contains("Carol"));
    }
}

使用should_panic检查是否恐慌

验证错误处理的情况

  • 测试除了验证代码的返回值是否正确,还需验证代码是否如预期的处理了发生错误的情况

  • 可验证代码在特定情况下是否发生了panic

  • should_panic属性(attribute):

  • 函数panic:测试通过

  • 函数没有panic:测试失败

pub struct Guess {
    value: u32,
}

impl Guess {
    pub fn new(value: u32) -> Guess {
        if value < 1 || value > 100 {
            panic!("Guess value must be between 1 and 100, got {}.", value)
        }

        Guess { value }
    }
}

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

    #[test]
    #[should_panic]
    fn greater_than_100() {
        Guess::new(200);
    }
}

让should_panic更精确

  • 让should_panic属性添加一个可选的expected参数:

  • 将检查失败消息中是否包含所指定的文字

在测试中使用Result<T, E>

在测试中使用Result<T, E>

  • 无需panic,可使用Result<T, E>作为返回类型编写测试:

  • 返回OK:测试通过

  • 返回Err:测试失败

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

    #[test]
    fn it_works() -> Result<(), String> {
        if 2 + 2 == 4 {
            Ok(())
        } else {
            Err(String::from("two plus two dose not equal four"))
        }
    }
}
  • 注意:不要在使用Result<T, E>编写的测试上标注#[should_panic]

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值