You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在Cargo二进制项目中正确组织单元测试?

如何在Cargo二进制项目中正确组织单元测试?

你遇到的这个问题其实是很多Rust新手都会踩的小坑——把测试代码错误地引入到了主二进制编译流程里,导致release构建时出现警告,还会把测试代码打包进最终的可执行文件里。别担心,Cargo本身就为我们设计了非常清晰的测试组织规范,下面给你讲几种最常用的正确做法:


一、最推荐:把单元测试直接写在被测试模块里(内联测试)

这种方式的核心是测试代码和被测试代码放在同一个文件里,但用#[cfg(test)]标记测试模块,告诉Cargo只有在运行cargo test的时候才编译这部分代码,release构建时会自动忽略。

具体修改步骤:

  1. 修改src/rectangle.rs,在文件末尾添加测试模块
#[derive(Debug)]
pub struct Rectangle {
    pub width: u32,
    pub height: u32,
}

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

// 新增:用#[cfg(test)]包裹测试模块,仅在测试时编译
#[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));
    }
}
  1. 修改src/main.rs,删除测试模块的引入
    把原来的代码改成:
mod rectangle;

fn main() {
    println!("Hello, world! Doing some rectangle stuff now...");
}

这样做的好处:

  • 测试代码和被测试逻辑紧挨着,后期维护时能快速找到对应测试
  • cargo build --release时完全不会编译测试代码,没有多余警告,二进制文件更干净
  • 运行cargo test时,Cargo会自动执行这个模块里的测试,结果和你之前的一致

二、适合集成测试:独立的tests/目录

如果你的测试是跨模块的集成测试(比如测试多个模块配合的功能),或者想把测试代码和业务代码完全分开,Cargo支持在项目根目录创建tests/文件夹,里面的所有.rs文件都会被自动识别为集成测试,不需要在主代码里做任何引入。

具体操作:

  1. 在项目根目录创建tests/文件夹,结构变成:
proj/
├── src/
│   ├── main.rs
│   └── rectangle.rs
├── tests/
│   └── rectangle_tests.rs
└── Cargo.toml
  1. tests/rectangle_tests.rs里写测试
// 注意这里要使用你的项目名(Cargo.toml里的`name`字段)来引入模块
use proj::rectangle::*;

#[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));
}
  1. 保持src/main.rs干净:不需要任何测试相关的引入,和上面的修改一样。

这样做的好处:

  • 测试代码和业务代码完全分离,适合大型项目的集成测试
  • Cargo会自动扫描tests/下的所有测试文件,运行cargo test时会同时执行模块内的单元测试和tests/下的集成测试
  • release构建完全不受测试代码影响

三、如果你坚持要把测试放在src/tests.rs

如果你不想把测试写在rectangle.rs里,也不想用tests/目录,只想保留src/tests.rs,那可以通过条件编译让Cargo只在测试时引入这个模块:

修改src/main.rs的测试模块引入:

mod rectangle;

// 只有在运行测试时才引入tests模块
#[cfg(test)]
mod tests;

fn main() {
    println!("Hello, world! Doing some rectangle stuff now...");
}

这样cargo build --release时会忽略mod tests;这一行,不会出现警告,也不会把测试代码编译进二进制。但这种方式不如前两种常用,因为测试和被测试代码分离较远,维护起来不太方便。


验证修改效果

现在你可以试试:

  • 运行cargo test:依然能正常执行所有测试,输出和之前一致
  • 运行cargo build --release:不会再出现任何关于mod tests;的警告,生成的二进制文件也不包含测试代码

这样就完美解决了你遇到的问题,同时符合Rust和Cargo的最佳实践~

火山引擎 最新活动