Rust模糊测试与模拟数据生成库blurmock的使用,高效生成随机数据并支持测试场景模拟

Rust模糊测试与模拟数据生成库blurmock的使用,高效生成随机数据并支持测试场景模拟

安装

在你的项目目录中运行以下Cargo命令:

cargo add blurmock

或者在Cargo.toml中添加以下行:

blurmock = "0.1.3"

基本用法

blurmock是一个Rust库,用于高效生成随机数据并支持测试场景模拟。以下是一个基本示例:

use blurmock::*;

#[derive(Debug, Mock)]
struct User {
    id: u64,
    name: String,
    email: String,
    active: bool,
    #[mock(faker="0..100")]
    age: u8,
}

fn main() {
    // 生成单个模拟用户
    let user: User = mock!();
    println!("Mock user: {:?}", user);
    
    // 生成多个模拟用户
    let users: Vec<User> = mock_vec!(3);
    for user in users {
        println!("User: {:?}", user);
    }
}

完整示例Demo

以下是一个更完整的示例,展示如何使用blurmock进行模糊测试和场景模拟:

use blurmock::*;

// 定义一个待测试的结构体
#[derive(Debug, Mock)]
struct Order {
    #[mock(faker="1..1000")]
    id: u64,
    customer_name: String,
    #[mock(faker="10.0..1000.0")]
    total_amount: f64,
    #[mock(faker="vec![1, 2, 3]")]
    items: Vec<u32>,
    is_completed: bool,
}

// 测试函数
fn process_order(order: &Order) -> Result<(), String> {
    if order.total_amount <= 0.0 {
        return Err("Total amount must be positive".to_string());
    }
    if order.customer_name.is_empty() {
        return Err("Customer name cannot be empty".to_string());
    }
    Ok(())
}

#[test]
fn test_order_processing() {
    // 生成100个随机订单进行模糊测试
    let orders: Vec<Order> = mock_vec!(100);
    
    for order in orders {
        match process_order(&order) {
            Ok(_) => {
                // 验证处理成功的订单满足条件
                assert!(order.total_amount > 0.0);
                assert!(!order.customer_name.is_empty());
            }
            Err(e) => {
                // 验证处理失败的订单确实违反条件
                assert!(
                    order.total_amount <= 0.0 || 
                    order.customer_name.is_empty(),
                    "Unexpected error: {}", e
                );
            }
        }
    }
}

// 自定义mock生成器示例
mod custom_mocks {
    use blurmock::MockContext;
    
    pub fn custom_status() -> String {
        let statuses = vec!["pending", "shipped", "delivered", "cancelled"];
        statuses[MockContext::random().gen_range(0..statuses.len())].to_string()
    }
}

#[derive(Debug, Mock)]
struct Shipment {
    #[mock(faker="1..1000")]
    tracking_number: u64,
    #[mock(using="custom_mocks::custom_status")]
    status: String,
}

fn main() {
    // 生成随机发货信息
    let shipment: Shipment = mock!();
    println!("Shipment status: {}", shipment.status);
    
    // 生成特定条件的模拟数据
    let completed_order = mock_with!(Order {
        is_completed: true,
        ..Default::default()
    });
    assert!(completed_order.is_completed);
}

文档

更多详细用法和API参考请查看官方文档。

许可证

BSD-2-Clause 许可证

所有者

Attila Dusnoki (dati91)


1 回复

Rust模糊测试与模拟数据生成库blurmock使用指南

介绍

blurmock是一个Rust库,专门用于模糊测试(Fuzz Testing)和模拟数据生成。它提供了高效生成随机数据的能力,并支持测试场景的模拟,特别适合以下场景:

  • 单元测试和集成测试中需要随机输入数据
  • 模糊测试需要生成大量随机数据来测试程序鲁棒性
  • 模拟真实世界数据分布进行压力测试
  • 快速创建测试用的模拟对象(Mock objects)

主要特性

  1. 高性能随机数据生成
  2. 可配置的数据生成规则
  3. 支持自定义数据类型生成
  4. 内置常见数据模式(邮件、URL、日期等)
  5. 与Rust测试框架无缝集成

基本使用方法

添加依赖

首先在Cargo.toml中添加依赖:

[dependencies]
blurmock = "0.3"

基本示例

use blurmock::prelude::*;

#[test]
fn test_random_data_generation() {
    // 初始化模糊测试生成器
    let mut fuzzer = Fuzzer::new();
    
    // 生成随机字符串
    let random_string: String = fuzzer.fuzz();
    println!("Random string: {}", random_string);
    
    // 生成指定长度的随机字符串
    let fixed_length_string: String = fuzzer.fuzz_with_len(10);
    assert_eq!(fixed_length_string.len(), 10);
    
    // 生成随机数字
    let random_number: i32 = fuzzer.fuzz();
    println!("Random number: {}", random_number);
    
    // 生成指定范围内的数字
    let ranged_number: u8 = fuzzer.fuzz_in_range(0..=100);
    assert!(ranged_number <= 100);
}

高级用法

自定义数据结构生成

use blurmock::prelude::*;

#[derive(Debug, Fuzz)]
struct User {
    #[fuzz(pattern = "[a-z]{5,10}")]
    username: String,
    #[fuzz(pattern = "email")]
    email: String,
    #[fuzz(range = "18..=99")]
    age: u8,
    #[fuzz(one_of = "admin,moderator,user")]
    role: String,
}

#[test]
fn test_custom_struct_generation() {
    let mut fuzzer = Fuzzer::new();
    let random_user: User = fuzzer.fuzz();
    
    println!("Random user: {:?}", random_user);
    assert!(random_user.age >= 18 && random_user.age <= 99);
}

模糊测试示例

use blurmock::prelude::*;

fn process_input(input: &str) -> Result<(), &'static str> {
    if input.len() > 100 {
        return Err("Input too long");
    }
    if input.contains('\0') {
        return Err("Null byte not allowed");
    }
    Ok(())
}

#[test]
fn fuzz_test_input_processing() {
    let mut fuzzer = Fuzzer::new()
        .string_config(StringConfig::new().max_len(150)); // 生成稍长于限制的字符串
    
    for _ in 0..1000 {
        let input: String = fuzzer.fuzz();
        let result = process_input(&input);
        
        // 验证函数是否正确处理了边界情况
        if input.len() > 100 {
            assert!(result.is_err());
        } else if input.contains('\0') {
            assert!(result.is_err());
        } else {
            assert!(result.is_ok());
        }
    }
}

模拟测试场景

use blurmock::prelude::*;

#[derive(Debug, Fuzz)]
struct Order {
    #[fuzz(range = "1..=1000")]
    id: u64,
    #[fuzz(pattern = "price")]
    amount: f64,
    #[fuzz(one_of = "pending,completed,cancelled")]
    status: String,
}

#[test]
fn test_order_processing() {
    let mut fuzzer = Fuzzer::new();
    
    // 生成100个随机订单测试处理逻辑
    let orders: Vec<Order> = fuzzer.fuzz_many(100);
    
    for order in orders {
        println!("Processing order: {:?}", order);
        // 这里添加实际的订单处理逻辑和断言
    }
}

配置选项

blurmock提供了丰富的配置选项来自定义数据生成行为:

use blurmock::{Fuzzer, StringConfig, NumberConfig};

let mut fuzzer = Fuzzer::new()
    .string_config(
        StringConfig::new()
            .min_len(5)
            .max_len(20)
            .exclude_chars("\0") // 排除空字符
    )
    .number_config(
        NumberConfig::new()
            .prefer_edge_cases(true) // 优先生成边界值
    );

最佳实践

  1. 在模糊测试中,建议运行至少1000次迭代以获得良好的覆盖率
  2. 对于自定义类型,使用#[derive(Fuzz)]可以简化实现
  3. 结合Rust的proptest库可以获得更强大的属性测试能力
  4. 对于关键业务逻辑,建议同时使用模糊测试和确定性测试

完整示例代码

以下是一个完整的blurmock使用示例,展示了如何创建自定义数据结构、配置生成器并进行模糊测试:

use blurmock::prelude::*;

// 自定义数据结构
#[derive(Debug, Fuzz)]
struct Product {
    #[fuzz(range = "1000..=9999")]
    id: u32,
    #[fuzz(pattern = "[A-Z][a-z]{5,12}")]
    name: String,
    #[fuzz(pattern = "price")]
    price: f64,
    #[fuzz(one_of = "available,out_of_stock,discontinued")]
    status: String,
    #[fuzz(range = "0..=5")]
    rating: u8,
}

// 被测函数
fn validate_product(product: &Product) -> Result<(), String> {
    if product.name.len() > 20 {
        return Err("Product name too long".to_string());
    }
    if product.price <= 0.0 {
        return Err("Price must be positive".to_string());
    }
    Ok(())
}

#[test]
fn test_product_validation() {
    // 配置模糊测试生成器
    let mut fuzzer = Fuzzer::new()
        .string_config(
            StringConfig::new()
                .min_len(5)
                .max_len(25) // 故意设置比验证条件稍大
        );
    
    // 运行1000次模糊测试
    for _ in 0..1000 {
        let product: Product = fuzzer.fuzz();
        let validation_result = validate_product(&product);
        
        // 验证边界条件
        if product.name.len() > 20 {
            assert!(validation_result.is_err());
        } else if product.price <= 0.0 {
            assert!(validation_result.is_err());
        } else {
            assert!(validation_result.is_ok());
        }
    }
}

#[test]
fn generate_product_catalog() {
    let mut fuzzer = Fuzzer::new();
    
    // 生成50个随机产品
    let products: Vec<Product> = fuzzer.fuzz_many(50);
    
    // 打印生成的第一个产品
    if let Some(first_product) = products.first() {
        println!("First generated product: {:?}", first_product);
    }
    
    // 验证所有产品ID都在指定范围内
    for product in &products {
        assert!(product.id >= 1000 && product.id <= 9999);
        assert!(product.rating <= 5);
    }
}

这个完整示例展示了:

  1. 定义自定义数据结构并使用#[derive(Fuzz)]自动实现模糊测试功能
  2. 配置模糊测试生成器的字符串生成规则
  3. 实现被测函数并验证其边界条件
  4. 批量生成测试数据并验证其有效性
回到顶部