Skip to content

Rust 测试

测试是保证代码质量的重要手段,Rust 提供了强大的测试支持。本章节将介绍 Rust 中的测试方法。

测试基础

单元测试

单元测试通常放在 src 目录中,与被测试的代码放在同一个文件中:

rust
// src/lib.rs
pub fn add(a: i32, b: i32) -> i32 {
    a + b
}

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_add() {
        assert_eq!(add(2, 3), 5);
        assert_eq!(add(-1, 1), 0);
        assert_eq!(add(0, 0), 0);
    }
}

集成测试

集成测试放在 tests 目录中:

src/
└── lib.rs
tests/
└── integration_test.rs
rust
// tests/integration_test.rs
use my_crate::add;

#[test]
fn test_add_integration() {
    assert_eq!(add(2, 3), 5);
    assert_eq!(add(-1, 1), 0);
    assert_eq!(add(0, 0), 0);
}

运行测试

运行所有测试

bash
cargo test

运行特定测试

bash
cargo test test_add

运行特定模块的测试

bash
cargo test tests::

显示测试输出

bash
cargo test -- --nocapture

测试断言

assert!

assert! 宏用于断言一个条件为真:

rust
#[test]
fn test_assert() {
    let value = true;
    assert!(value);
}

assert_eq! 和 assert_ne!

assert_eq! 宏用于断言两个值相等,assert_ne! 宏用于断言两个值不相等:

rust
#[test]
fn test_assert_eq() {
    assert_eq!(2 + 2, 4);
    assert_ne!(2 + 2, 5);
}

assert!(with message)

可以为断言添加自定义消息:

rust
#[test]
fn test_with_message() {
    let result = 2 + 2;
    assert_eq!(result, 4, "2 + 2 should equal 4, but got {}", result);
}

测试失败

预期失败的测试

使用 #[should_panic] 属性标记预期会失败的测试:

rust
#[test]
#[should_panic]
fn test_panic() {
    panic!("This test should panic");
}

测试结果

测试函数可以返回 Result<(), E> 类型:

rust
#[test]
fn test_result() -> Result<(), String> {
    if 2 + 2 == 4 {
        Ok(())
    } else {
        Err(String::from("2 + 2 should equal 4"))
    }
}

测试组织

测试模块

使用模块组织测试:

rust
#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_add() {
        assert_eq!(add(2, 3), 5);
    }
    
    #[test]
    fn test_subtract() {
        assert_eq!(subtract(5, 3), 2);
    }
}

测试工具函数

在测试模块中添加工具函数:

rust
#[cfg(test)]
mod tests {
    use super::*;
    
    fn setup() {
        // 测试前的设置
    }
    
    #[test]
    fn test_add() {
        setup();
        assert_eq!(add(2, 3), 5);
    }
}

模拟和依赖注入

使用 mockall 库

toml
[dependencies]
mockall = "0.11"
rust
// src/lib.rs
pub trait Database {
    fn get_user(&self, id: u32) -> Option<String>;
}

pub fn get_username(db: &dyn Database, id: u32) -> String {
    db.get_user(id).unwrap_or_else(|| "Unknown".to_string())
}

#[cfg(test)]
mod tests {
    use super::*;
    use mockall::mock;
    
    mock! {
        pub Database {
            fn get_user(&self, id: u32) -> Option<String>;
        }
    }
    
    #[test]
    fn test_get_username() {
        let mut mock_db = MockDatabase::new();
        mock_db.expect_get_user()
            .with(eq(1))
            .returning(|_| Some("Alice".to_string()));
        
        assert_eq!(get_username(&mock_db, 1), "Alice");
    }
}

总结

  • 单元测试:放在 src 目录中,与被测试的代码放在同一个文件中
  • 集成测试:放在 tests 目录中
  • 运行测试:使用 cargo test 命令
  • 测试断言assert!assert_eq!assert_ne!
  • 测试失败#[should_panic]Result<(), E>
  • 测试组织:使用模块组织测试
  • 模拟和依赖注入:使用 mockall

测试是保证代码质量的重要手段,Rust 提供了强大的测试支持。通过本章节的学习,你应该已经掌握了 Rust 中的测试方法。