Rust参数化测试库parameterized的使用,简化多参数测试用例管理与执行的Rust插件库

Rust参数化测试库parameterized的使用

parameterized是一个基于procedural macro的参数化测试库,当您需要用一个测试函数运行多种不同的输入组合时非常有用。

基本用法

在定义参数化测试用例时,应使用#[parameterized(...)]属性代替#[test]。这个库的灵感来自JUnit的@ParameterizedTest

示例代码

// 定义水果枚举
enum Fruit {
    Apple,
    Bramble(BrambleFruit),
    Pear,
}

// 定义获取名称的trait
trait NameOf {
    fn name_of(&self) -> &str;
}

// 为Fruit实现NameOf
impl NameOf for Fruit {
    fn name_of(&self) -> &str {
        match self {
            Fruit::Apple => "apple",
            Fruit::Bramble(fruit) => fruit.name_of(),
            Fruit::Pear => "pear",
        }
    }
}

// 定义莓果枚举
enum BrambleFruit {
    Blackberry,
}

// 为BrambleFruit实现NameOf
impl NameOf for BrambleFruit {
    fn name_of(&self) -> &str {
        match self {
            BrambleFruit::Blackberry => "blackberry",
        }
    }
}

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

    // 参数化测试示例
    #[parameterized(fruit = {
        Fruit::Apple, 
        Fruit::Pear, 
        Fruit::Bramble(BrambleFruit::Blackberry)
    }, name = {
        "apple", "pear", "blackberry"
    })]
    fn a_fruity_test(fruit: Fruit, name: &str) {
        assert_eq!(fruit.name_of(), name)
    }
}

自定义测试属性

默认情况下,parameterized属性会生成带有#[test]属性的测试用例。但有时需要使用不同的测试宏,比如#[tokio::test]

使用自定义测试宏

use parameterized::parameterized;

// 异步参数化测试示例
#[parameterized(input = {
    0, 1
}, expected = {
    5, 6
})]
#[parameterized_macro(tokio::test)]  // 使用tokio测试宏
async fn add5(input: u32, expected: u32) {
    assert_eq!(input + 5, expected);
}

注意事项:

  1. #[parameterized_macro(...)]必须始终在#[parameterized(...)]属性之后指定
  2. 目前只支持每个参数化测试函数一个#[parameterized_macro(...)]属性

IDE运行测试问题

由于IntelliJ IDEA目前不扩展属性宏,因此不会识别由parameterized宏生成的测试用例。可以使用以下解决方法:

#[cfg(test)]
mod tests {
    use super::*;
    use parameterized::parameterized as pm;  // 使用别名
    use parameterized::ide;  // 导入ide模块
        
    mod squared_tests {
        use super::*;

        ide!();  // 使用ide宏
    
        // 参数化测试
        #[pm(input = {
            -2, -1, 0, 1, 2
        }, expected = {
            4, 1, 0, 1, 4
        })]
        fn test_squared(input: i8, output: i8) {
            assert_eq(squared(input), output);
        }
    }
}

完整示例

下面是一个完整的参数化测试示例,展示了如何测试一个简单的平方函数:

use parameterized::parameterized;

// 平方函数
fn squared(input: i32) -> i32 {
    input * input
}

#[cfg(test)]
mod tests {
    use super::*;
    
    // 普通参数化测试
    #[parameterized(input = {
        -2, -1, 0, 1, 2
    }, expected = {
        4, 1, 0, 1, 4
    })]
    fn test_squared(input: i32, expected: i32) {
        assert_eq!(squared(input), expected);
    }
    
    // 异步参数化测试示例
    #[parameterized(input = {
        1, 2, 3
    }, expected = {
        1, 4, 9
    })]
    #[parameterized_macro(tokio::test)]  // 使用tokio测试宏
    async fn test_squared_async(input: i32, expected: i32) {
        assert_eq!(squared(input), expected);
    }
}

导入方式

如果您不想在每个测试模块中都导入这个库,可以在您的crate根目录顶部添加以下代码:

#[cfg(test)]
#[macro_use]  // 全局导入宏
extern crate parameterized;

许可证

parameterized采用双许可证:Apache License 2.0或MIT license。


1 回复

Rust参数化测试库parameterized使用指南

parameterized是一个简化Rust中多参数测试用例管理与执行的库,它允许你使用简洁的语法为同一个测试函数提供多组输入参数。

安装

Cargo.toml中添加依赖:

[dev-dependencies]
parameterized = "0.5.1"

基本用法

1. 简单参数化测试

use parameterized::parameterized;

#[parameterized(input = {1, 2, 3}, expected = {2, 3, 4})]
fn test_add_one(input: i32, expected: i32) {
    assert_eq!(input + 1, expected);
}

2. 使用元组作为参数

#[parameterized(
    input = {
        (1, 2),
        (3, 4),
        (5, 6)
    },
    expected = {3, 7, 11}
)]
fn test_add(input: (i32, i32), expected: i32) {
    assert_eq!(input.0 + input.1, expected);
}

3. 使用自定义类型

struct Point {
    x: i32,
    y: i32,
}

#[parameterized(
    input = {
        Point { x: 1, y: 2 },
        Point { x: 3, y: 4 }
    },
    expected = {3, 7}
)]
fn test_point_sum(input: Point, expected: i32) {
    assert_eq!(input.x + input.y, expected);
}

高级用法

1. 使用表达式生成参数

#[parameterized(input = {
    2 * 1,
    2 * 2,
    2 * 3
}, expected = {2, 4, 6})]
fn test_double(input: i32, expected: i32) {
    assert_eq!(input, expected);
}

2. 使用范围生成参数

#[parameterized(input = 1..=5)]
fn test_is_positive(input: i32) {
    assert!(input > 0);
}

3. 组合多个参数集

#[parameterized(
    a = {1, 2},
    b = {3, 4}
)]
fn test_multiple_params(a: i32, b: i32) {
    assert!(a + b > 0);
}

与标准测试框架集成

parameterized可以与Rust的标准测试框架无缝集成:

#[cfg(test)]
mod tests {
    use super::*;
    use parameterized::parameterized;
    
    #[parameterized(input = {1, 2, 3}, expected = {2, 3, 4})]
    fn test_add_one(input: i32, expected: i32) {
        assert_eq!(input + 1, expected);
    }
}

测试输出

运行测试时,每个参数组合会作为独立的测试用例显示:

running 3 tests
test test_add_one::case_1 ... ok
test test_add_one::case_2 ... ok
test test_add_one::case_3 ... ok

优势

  1. 减少重复代码:避免为相似的测试用例编写多个几乎相同的测试函数
  2. 提高可读性:所有测试用例集中在一个地方,便于查看和维护
  3. 灵活的参数组合:支持各种类型的参数和复杂的参数生成方式

parameterized库特别适合需要测试多种输入组合的场景,如边界值测试、等价类划分测试等测试设计技术。

完整示例Demo

下面是一个完整的Rust测试模块示例,展示了如何使用parameterized库进行参数化测试:

// 在Cargo.toml中添加:
// [dev-dependencies]
// parameterized = "0.5.1"

#[cfg(test)]
mod tests {
    use parameterized::parameterized;
    
    // 1. 简单参数化测试示例
    #[parameterized(input = {1, 2, 3}, expected = {2, 3, 4})]
    fn test_add_one(input: i32, expected: i32) {
        // 测试输入值加1是否等于预期值
        assert_eq!(input + 1, expected);
    }
    
    // 2. 使用元组作为参数的示例
    #[parameterized(
        input = {
            (1, 2),
            (3, 4),
            (5, 6)
        },
        expected = {3, 7, 11}
    )]
    fn test_add_tuples(input: (i32, i32), expected: i32) {
        // 测试元组中两个数相加的结果
        assert_eq!(input.0 + input.1, expected);
    }
    
    // 3. 使用自定义类型的示例
    #[derive(Debug)]
    struct Point {
        x: i32,
        y: i32,
    }
    
    #[parameterized(
        point = {
            Point { x: 1, y: 2 },
            Point { x: 3, y: 4 }
        },
        expected = {3, 7}
    )]
    fn test_point_sum(point: Point, expected: i32) {
        // 测试Point结构体的x和y相加结果
        assert_eq!(point.x + point.y, expected);
    }
    
    // 4. 高级用法:使用表达式生成参数
    #[parameterized(input = {
        2 * 1,
        2 * 2,
        2 * 3
    }, expected = {2, 4, 6})]
    fn test_expressions(input: i32, expected: i32) {
        // 测试输入值是否等于预期值
        assert_eq!(input, expected);
    }
    
    // 5. 高级用法:使用范围生成参数
    #[parameterized(num = 1..=5)]
    fn test_range(num: i32) {
        // 测试范围内的数是否都大于0
        assert!(num > 0);
    }
    
    // 6. 高级用法:组合多个参数集
    #[parameterized(
        a = {1, 2},
        b = {3, 4}
    )]
    fn test_multiple_parameters(a: i32, b: i32) {
        // 测试多参数组合的和是否大于0
        assert!(a + b > 0);
    }
}

这个完整示例展示了parameterized库的主要功能,包括:

  • 基本参数化测试
  • 元组参数测试
  • 自定义类型测试
  • 表达式生成参数
  • 范围生成参数
  • 多参数组合测试

要运行这些测试,只需执行常规的cargo test命令,每个参数组合都会作为独立的测试用例运行。

回到顶部