Rust宏测试库macrotest的使用,Rust宏测试库macrotest提供高效的宏单元测试和代码覆盖率分析

Rust宏测试库macrotest的使用

macrotest是一个类似于trybuild的Rust测试库,但它允许您测试声明式宏或过程宏是如何展开的。它提供了高效的宏单元测试和代码覆盖率分析功能。

基本使用

首先需要安装cargo-expand。

在您的Crate的Cargo.toml中添加:

[dev-dependencies]
macrotest = "1"

在您的crate的tests/目录下,创建tests.rs文件,包含以下代码:

#[test]
pub fn pass() {
    macrotest::expand("tests/expand/*.rs");
}

然后创建tests/expand/目录,并在其中添加Rust源文件。每个源文件都是一个宏展开测试用例。

完整示例

下面是一个完整的示例项目结构:

my_macro_crate/
├── Cargo.toml
├── src/
│   └── lib.rs
└── tests/
    ├── expand/
    │   └── my_macro_test.rs
    └── tests.rs

src/lib.rs

// 定义一个简单的宏
#[macro_export]
macro_rules! greet {
    ($name:expr) => {
        println!("Hello, {}!", $name);
    };
}

tests/expand/my_macro_test.rs

use my_macro_crate::greet;

fn main() {
    greet!("world");
}

tests/tests.rs

#[test]
pub fn test_greet_macro_expansion() {
    macrotest::expand("tests/expand/*.rs");
}

运行测试时,macrotest会检查宏是否正确展开,并与预期的结果进行比较。

功能特点

  1. 宏展开验证:确保宏按照预期展开
  2. 代码覆盖率:帮助分析宏测试的覆盖率
  3. 错误处理:可以测试宏的错误情况
  4. 与cargo-expand集成:利用cargo-expand的功能

最低支持的Rust版本

macrotest要求的最低Rust版本是1.66.0。

许可证

macrotest采用MIT或Apache-2.0双重许可证。

完整示例代码

以下是一个更完整的macrotest使用示例:

Cargo.toml

[package]
name = "my_macro_crate"
version = "0.1.0"
edition = "2021"

[lib]
proc-macro = true

[dev-dependencies]
macrotest = "1.0.9"

src/lib.rs

// 定义一个更复杂的宏,带有多个匹配模式
#[macro_export]
macro_rules! calculate {
    // 加法
    ($a:expr + $b:expr) => {
        $a + $b
    };
    // 减法
    ($a:expr - $b:expr) => {
        $a - $b
    };
    // 默认返回0
    () => {
        0
    };
}

// 过程宏示例
#[proc_macro]
pub fn make_answer(_item: proc_macro::TokenStream) -> proc_macro::TokenStream {
    "fn answer() -> u32 { 42 }".parse().unwrap()
}

tests/expand/calculate_test.rs

use my_macro_crate::calculate;

#[test]
fn test_calculate_macro() {
    let sum = calculate!(10 + 5);
    assert_eq!(sum, 15);
    
    let diff = calculate!(20 - 8);
    assert_eq!(diff, 12);
    
    let zero = calculate!();
    assert_eq!(zero, 0);
}

tests/expand/proc_macro_test.rs

use my_macro_crate::make_answer;

make_answer!();

#[test]
fn test_proc_macro() {
    assert_eq!(answer(), 42);
}

tests/tests.rs

#[test]
fn test_macros_expansion() {
    // 测试声明式宏
    macrotest::expand("tests/expand/calculate_test.rs");
    // 测试过程宏
    macrotest::expand("tests/expand/proc_macro_test.rs");
}

1 回复

Rust宏测试库macrotest的使用指南

介绍

macrotest是一个专门为Rust宏设计的测试库,它提供了高效的宏单元测试和代码覆盖率分析功能。这个库特别适合测试过程宏(proc-macro)和声明宏(declarative macro),能够帮助开发者确保宏在各种输入情况下的正确行为。

主要特性

  • 支持对宏展开结果的精确测试
  • 提供详细的代码覆盖率分析
  • 能够捕获和测试宏编译错误
  • 支持多文件测试场景
  • 与标准Rust测试框架无缝集成

完整示例Demo

以下是基于内容中提供的示例,组合而成的完整演示项目:

  1. 首先创建项目并添加依赖:
cargo new macro_demo
cd macro_demo
  1. 编辑Cargo.toml文件:
[package]
name = "macro_demo"
version = "0.1.0"
edition = "2021"

[lib]
proc-macro = true  # 如果是过程宏需要这个

[dev-dependencies]
macrotest = "1.0"
  1. 创建宏实现文件src/lib.rs
// 声明宏示例
#[macro_export]
macro_rules! greet {
    ($name:expr) => {
        format!("Hello, {}!", $name)
    };
}

// 过程宏示例
use proc_macro::TokenStream;

#[proc_macro_derive(MyDerive)]
pub fn my_derive(input: TokenStream) -> TokenStream {
    // 简单的derive宏实现
    "impl Example { fn new() -> Self { Self { field: String::new() } } }"
        .parse()
        .unwrap()
}
  1. 创建测试目录结构和测试文件:
tests/
├── expand/
│   ├── greet.rs
│   └── derive.rs
├── compile-errors/
│   └── invalid_greet.rs
├── greet_test.rs
├── compile_error_test.rs
└── derive_test.rs
  1. 编写测试文件tests/greet_test.rs
use macrotest::expand;

#[test]
fn test_greet_macro() {
    // 测试greet宏的展开
    expand!("tests/expand/greet.rs");
}
  1. 编写宏输入文件tests/expand/greet.rs
use macro_demo::greet;

fn main() {
    let _ = greet!("World");  // 测试正常输入
}
  1. 编写错误测试文件tests/compile_error_test.rs
use macrotest::compile_error;

#[test]
fn test_invalid_greet() {
    // 测试宏的错误输入
    compile_error!("tests/compile-errors/invalid_greet.rs");
}
  1. 编写错误输入文件tests/compile-errors/invalid_greet.rs
use macro_demo::greet;

fn main() {
    let _ = greet!();  // 缺少参数,应该报错
}
  1. 编写过程宏测试文件tests/derive_test.rs
use macrotest::expand;

#[test]
fn test_derive_macro() {
    // 测试derive宏的展开
    expand!("tests/expand/derive.rs");
}
  1. 编写derive宏输入文件tests/expand/derive.rs
use macro_demo::MyDerive;

#[derive(MyDerive)]
struct Example {
    field: String,
}

fn main() {}
  1. (可选)创建配置文件tests/macrotest.toml
# 设置测试环境
rustc_args = ["--edition=2021"]

# 设置宏展开后代码的格式化选项
pretty = true
  1. 运行测试:
# 首次运行生成预期结果
UPDATE_EXPECT=1 cargo test

# 后续正常测试
cargo test

# 收集覆盖率
cargo install cargo-tarpaulin
cargo tarpaulin --tests --features macrotest/coverage

注意事项

  1. 确保测试文件位于tests/目录下
  2. 对于过程宏,需要在Cargo.toml中正确声明proc-macro特性
  3. 更新预期结果时,请仔细检查生成的代码
  4. 复杂的宏可能需要更详细的测试用例来覆盖所有边界情况

macrotest为Rust宏提供了强大的测试能力,能够显著提高宏代码的质量和可靠性。

回到顶部