Rust PostgreSQL扩展开发框架pgrx-tests的使用,pgrx-tests助力高效PostgreSQL插件测试与验证

Rust PostgreSQL扩展开发框架pgrx-tests的使用,pgrx-tests助力高效PostgreSQL插件测试与验证

pgrx-tests是用于pgrx的测试框架,建议作为[dev-dependencies]与pgrx一起使用。

安装

全局安装二进制工具

cargo install pgrx-tests

运行上述命令将全局安装pgrx_embed_pgrx-tests二进制工具。

作为库安装

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

cargo add pgrx-tests

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

pgrx-tests = "0.16.0"

完整示例demo

下面是一个使用pgrx-tests进行PostgreSQL扩展测试的完整示例:

// 在Cargo.toml中添加依赖
// [dev-dependencies]
// pgrx-tests = "0.16.0"

use pgrx::prelude::*;
use pgrx_tests::testing::*;

// 定义要测试的PostgreSQL扩展函数
#[pg_extern]
fn add_one(i: i32) -> i32 {
    i + 1
}

// 定义测试模块
#[cfg(test)]
mod tests {
    use super::*;
    use pgrx_tests::pg_test;

    // 定义测试函数
    #[pg_test]
    fn test_add_one() {
        // 测试add_one函数
        let result = Spi::get_one::<i32>("SELECT add_one(1);");
        assert_eq!(result, Ok(Some(2)));
    }

    // 另一个测试示例
    #[pg_test]
    fn test_add_one_with_negative() {
        let result = Spi::get_one::<i32>("SELECT add_one(-5);");
        assert_eq!(result, Ok(Some(-4)));
    }
}

测试执行

要运行这些测试,可以使用以下命令:

cargo pgrx test

完整示例代码

基于上述示例,这里提供一个更完整的PostgreSQL扩展测试示例:

// 在Cargo.toml中添加依赖
// [dev-dependencies]
// pgrx-tests = "0.16.0"

use pgrx::prelude::*;
use pgrx_tests::testing::*;

// 定义一个简单的数学函数库扩展

// 加法函数
#[pg_extern]
fn add(a: i32, b: i32) -> i32 {
    a + b
}

// 减法函数
#[pg_extern]
fn subtract(a: i32, b: i32) -> i32 {
    a - b
}

// 阶乘函数
#[pg_extern]
fn factorial(n: i32) -> i64 {
    if n <= 1 {
        1
    } else {
        (1..=n as i64).product()
    }
}

// 测试模块
#[cfg(test)]
mod tests {
    use super::*;
    use pgrx_tests::pg_test;

    // 测试加法函数
    #[pg_test]
    fn test_add() {
        let result = Spi::get_one::<i32>("SELECT add(10, 20);");
        assert_eq!(result, Ok(Some(30)));
    }

    // 测试减法函数
    #[pg_test]
    fn test_subtract() {
        let result = Spi::get_one::<i32>("SELECT subtract(50, 20);");
        assert_eq!(result, Ok(Some(30)));
    }

    // 测试阶乘函数
    #[pg_test]
    fn test_factorial() {
        let result = Spi::get_one::<i64>("SELECT factorial(5);");
        assert_eq!(result, Ok(Some(120)));
    }

    // 测试边界条件
    #[pg_test]
    fn test_factorial_boundary() {
        let result = Spi::get_one::<i64>("SELECT factorial(0);");
        assert_eq!(result, Ok(Some(1)));
    }

    // 测试多个函数的组合使用
    #[pg_test]
    fn test_combined_operations() {
        let result = Spi::get_one::<i32>(
            "SELECT add(subtract(100, 50), subtract(50, 10));"
        );
        assert_eq!(result, Ok(Some(90)));
    }
}

1 回复

Rust PostgreSQL扩展开发框架pgrx-tests的使用指南

概述

pgrx-tests是pgrx框架的一部分,专门用于简化PostgreSQL扩展的测试流程。它为Rust开发者提供了一个高效的方式来编写和运行PostgreSQL扩展的单元测试和集成测试。

主要特性

  • 简化PostgreSQL扩展测试设置
  • 自动管理测试数据库生命周期
  • 支持并行测试执行
  • 提供方便的断言宏
  • 与标准Rust测试工具集成

安装与设置

首先确保已安装pgrx工具链:

cargo install --locked cargo-pgrx
cargo pgrx init

然后在你的pgrx项目中添加依赖:

[dependencies]
pgrx = "0.9"

基本使用方法

1. 创建测试模块

在你的扩展项目中创建一个测试模块:

#[cfg(test)]
mod tests {
    use pgrx::prelude::*;
    use pgrx_tests::pg_test;
    
    #[pg_test]
    fn test_my_extension_function() {
        // 测试代码
    }
}

2. 编写测试用例

#[pg_test]
fn test_add_numbers() {
    Spi::connect(|client| {
        let result = client.select("SELECT my_add_function(1, 2);", None, None)
            .first()
            .get_one::<i32>();
        assert_eq!(result, Ok(3));
    });
}

3. 运行测试

使用cargo pgrx运行测试:

cargo pgrx test

高级功能

1. 测试初始化

#[pg_test(initialization = "init_test_data")]
fn test_with_data() {
    // 测试代码
}

fn init_test_data() {
    Spi::connect(|client| {
        client.update("INSERT INTO test_table VALUES (1, 'test');", None, None);
    });
}

2. 并行测试

#[pg_test(parallel)]
fn test_parallel_1() {
    // 测试代码
}

#[pg_test(parallel)]
fn test_parallel_2() {
    // 测试代码
}

3. 事务处理

#[pg_test(transaction)]
fn test_in_transaction() {
    // 测试代码将在事务中运行
}

实际示例

以下是一个完整的测试示例,测试一个自定义聚合函数:

#[pg_test]
fn test_custom_aggregate() {
    Spi::connect(|client| {
        // 创建测试表
        client.update("CREATE TABLE test_agg (value integer);", None, None);
        
        // 插入测试数据
        for i in 1..=5 {
            client.update(&format!("INSERT INTO test_agg VALUES ({});", i), None, None);
        }
        
        // 测试自定义聚合
        let result = client.select("SELECT my_custom_agg(value) FROM test_agg;", None, None)
            .first()
            .get_one::<i32>();
            
        assert_eq(result, Ok(15)); // 假设我们的聚合函数是求和
        
        // 清理
        client.update("DROP TABLE test_agg;", None, None);
    });
}

完整示例demo

下面是一个完整的pgrx扩展测试示例,包含多个测试场景:

// src/lib.rs
use pgrx::prelude::*;

#[pg_extern]
fn add_numbers(a: i32, b: i32) -> i32 {
    a + b
}

#[pg_extern]
fn string_length(s: &str) -> i32 {
    s.len() as i32
}

#[cfg(test)]
mod tests {
    use super::*;
    use pgrx::prelude::*;
    use pgrx_tests::pg_test;

    // 测试初始化函数
    fn setup_test_table() {
        Spi::connect(|client| {
            client.update("CREATE TABLE test_data (id serial, name text);", None, None);
            client.update("INSERT INTO test_data (name) VALUES ('test1'), ('test2');", None, None);
        });
    }

    // 基本功能测试
    #[pg_test]
    fn test_add_numbers() {
        Spi::connect(|client| {
            let result = client.select("SELECT add_numbers(1, 2);", None, None)
                .first()
                .get_one::<i32>();
            assert_eq!(result, Ok(3));
        });
    }

    // 字符串函数测试
    #[pg_test]
    fn test_string_length() {
        Spi::connect(|client| {
            let result = client.select("SELECT string_length('hello');", None, None)
                .first()
                .get_one::<i32>();
            assert_eq!(result, Ok(5));
        });
    }

    // 带初始化的测试
    #[pg_test(initialization = "setup_test_table")]
    fn test_table_operations() {
        Spi::connect(|client| {
            // 验证表创建和数据插入
            let count = client.select("SELECT COUNT(*) FROM test_data;", None, None)
                .first()
                .get_one::<i64>();
            assert_eq!(count, Ok(2));
            
            // 测试数据内容
            let name = client.select("SELECT name FROM test_data WHERE id = 1;", None, None)
                .first()
                .get_one::<String>();
            assert_eq!(name, Ok("test1".to_string()));
        });
    }

    // 并行测试
    #[pg_test(parallel)]
    fn test_parallel_1() {
        Spi::connect(|client| {
            let result = client.select("SELECT add_numbers(10, 20);", None, None)
                .first()
                .get_one::<i32>();
            assert_eq!(result, Ok(30));
        });
    }

    #[pg_test(parallel)]
    fn test_parallel_2() {
        Spi::connect(|client| {
            let result = client.select("SELECT string_length('parallel');", None, None)
                .first()
                .get_one::<i32>();
            assert_eq!(result, Ok(8));
        });
    }

    // 事务测试
    #[pg_test(transaction)]
    fn test_in_transaction() {
        Spi::connect(|client| {
            // 这个操作会在事务中执行,测试完成后自动回滚
            client.update("INSERT INTO test_data (name) VALUES ('temp');", None, None);
            let count = client.select("SELECT COUNT(*) FROM test_data;", None, None)
                .first()
                .get_one::<i64>();
            assert_eq!(count, Ok(3));
        });
    }
}

最佳实践

  1. 隔离测试:每个测试应该独立运行,不依赖其他测试的状态
  2. 清理资源:测试后清理创建的表或其他对象
  3. 使用事务:对于可能失败的测试,使用事务避免污染数据库状态
  4. 组合测试:将相关测试组织在一起,使用模块化结构
  5. 性能考虑:对于耗时测试,标记为#[ignore]并在需要时单独运行

调试技巧

如果测试失败,可以:

  1. 使用cargo pgrx test -- --nocapture查看完整输出
  2. 检查PostgreSQL日志获取更多信息
  3. 使用#[pg_test(verbose)]获取更详细的执行信息

pgrx-tests框架通过简化PostgreSQL扩展的测试流程,显著提高了开发效率和代码质量,是开发高质量PostgreSQL扩展的必备工具。

回到顶部