Rust的Holochain WASM测试工具库holochain_wasm_test_utils使用指南,简化WASM智能合约单元测试开发

Rust的Holochain WASM测试工具库holochain_wasm_test_utils使用指南,简化WASM智能合约单元测试开发

Wasm测试工具

这个crate包含:

  • 几个编译成Wasm的小crate,用作测试值
  • enum TestWasm枚举所有这些crate
  • impl From<TestWasm> for DnaWasm获取这些crate的编译后Wasm工件
  • 一个build.rs文件,在编译时将所有这些crate构建并包含到库中

这些Wasm crate直接测试Holochain的主机/访客实现,而不通过HDK或其他便利接口。这样做是为了确保不使用hdkholochain_wasmer_* crate时也能相对容易地与Holochain交互。

运行这些Wasm的测试通常位于核心的ribosome.rs模块中。这是必要的,因为Wasm crate依赖于核心定义的某些全局函数,需要注入。

安装方法

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

cargo add holochain_wasm_test_utils

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

holochain_wasm_test_utils = "0.5.4"

完整示例代码

use holochain_wasm_test_utils::TestWasm;
use holochain_zome_types::DnaWasm;

// 获取测试Wasm的路径
let test_wasm = TestWasm::Foo;  // 使用Foo作为示例测试Wasm
let wasm_path: DnaWasm = test_wasm.into();

// 在测试中使用Wasm
#[test]
fn test_wasm_execution() {
    // 初始化测试环境
    let mut conductor = Conductor::builder().build();
    
    // 创建包含测试Wasm的DNA
    let dna = DnaFile::new(
        DnaDef::unique(),
        vec![wasm_path]
    ).unwrap();
    
    // 安装DNA到导体
    let cell_id = conductor.install_dna(dna).unwrap();
    
    // 调用Wasm函数进行测试
    let result: SerializedBytes = conductor.call(
        &cell_id,
        "zome_name",
        "function_name",
        None,
        &()  // 传入参数
    ).unwrap();
    
    // 断言结果
    assert!(result.is_valid());
}

示例说明

  1. 首先导入必要的类型:TestWasm枚举和DnaWasm类型
  2. 选择一个测试Wasm(这里使用Foo作为示例)
  3. TestWasm转换为DnaWasm获取路径
  4. 在测试函数中:
    • 创建导体(Conductor)实例
    • 构建包含测试Wasm的DNA定义
    • 将DNA安装到导体中
    • 调用Wasm函数进行测试
    • 验证结果

这个示例展示了如何使用holochain_wasm_test_utils来简化Wasm智能合约的单元测试开发,通过预构建的测试Wasm来验证核心功能。

元数据

  • 版本: 0.5.4
  • 发布时间: 28天前
  • 许可证: Apache-2.0
  • 大小: 7.48 KiB

扩展完整示例

use holochain_wasm_test_utils::TestWasm;
use holochain_zome_types::{DnaWasm, DnaDef, DnaFile};
use holochain_conductor_api::{Conductor, ConductorHandle};
use holochain_types::prelude::{SerializedBytes, InstallAppPayload};

// 测试多个Wasm模块的示例
#[tokio::test]
async fn test_multiple_wasm_modules() {
    // 初始化导体
    let conductor = Conductor::builder().build().await.unwrap();
    
    // 准备多个测试Wasm
    let wasms = vec![
        TestWasm::Foo.into(),
        TestWasm::Bar.into(),
        TestWasm::Baz.into()
    ];
    
    // 创建DNA定义
    let dna = DnaFile::new(
        DnaDef::unique().with_wasms(wasms),
        Default::default()
    ).unwrap();
    
    // 安装应用
    let payload = InstallAppPayload {
        installed_app_id: "test_app".to_string(),
        dna: dna.clone(),
        agent_key: conductor.keystore().new_sign_keypair_random().unwrap(),
        membrane_proofs: Default::default(),
    };
    
    let cell_id = conductor.install_app(payload).await.unwrap();
    
    // 测试Foo模块
    let foo_result: SerializedBytes = conductor.call(
        &cell_id,
        "foo_zome",
        "foo_fn",
        None,
        &()
    ).await.unwrap();
    
    // 测试Bar模块
    let bar_result: SerializedBytes = conductor.call(
        &cell_id,
        "bar_zome",
        "bar_fn",
        None,
        &()
    ).await.unwrap();
    
    // 断言结果
    assert!(foo_result.is_valid());
    assert!(bar_result.is_valid());
}

扩展示例说明

  1. 这个扩展示例展示了如何测试多个Wasm模块
  2. 使用TestWasm枚举的不同变体(Foo, Bar, Baz)
  3. 创建包含多个Wasm的DNA
  4. 安装应用到导体
  5. 分别测试不同Wasm模块的功能
  6. 使用异步测试(Tokio运行时)
  7. 包含更完整的错误处理和类型转换

这个示例演示了如何在更复杂的场景下使用holochain_wasm_test_utils进行集成测试。


1 回复

Rust的Holochain WASM测试工具库holochain_wasm_test_utils使用指南

简介

holochain_wasm_test_utils是一个专门为Holochain WASM智能合约设计的测试工具库,它简化了单元测试的开发流程,让开发者能够更高效地测试他们的WASM合约。

这个库提供了一系列实用工具和模拟环境,使开发者能够在不需要完整Holochain运行时的情况下测试他们的WASM代码。

主要特性

  • 提供模拟的Holochain运行时环境
  • 简化WASM函数的调用和测试
  • 支持对zome函数的单元测试
  • 提供便捷的断言工具
  • 支持测试数据生成

安装方法

在Cargo.toml的dev-dependencies中添加:

[dev-dependencies]
holochain_wasm_test_utils = "0.2.0"  # 请使用最新版本

完整示例代码

下面是一个完整的测试示例,展示了如何使用holochain_wasm_test_utils测试一个简单的博客应用:

// 测试模块
#[cfg(test)]
mod tests {
    use holochain_wasm_test_utils::{TestWasm, TestContext, TestChain};
    use serde_json::json;

    // 测试创建博客文章
    #[test]
    fn test_create_blog_post() {
        // 1. 设置测试环境
        let mut test_wasm = TestWasm::from_file("path/to/blog_zome.wasm");
        
        // 2. 设置测试上下文
        let context = TestContext::new()
            .with_agent("test_author_1")
            .with_dna("blog_dna_hash");
        test_wasm.set_context(context);
        
        // 3. 调用创建博客文章函数
        let post_data = json!({
            "title": "Rust测试指南",
            "content": "这是一篇关于如何使用holochain_wasm_test_utils的博客文章",
            "category": "技术"
        });
        
        let result: String = test_wasm
            .call("blog_zome", "create_post", post_data.to_string())
            .unwrap();
        
        // 4. 验证返回结果
        assert!(!result.is_empty());
        
        // 5. 验证链上数据
        let mut chain = TestChain::new();
        chain.add_entry("blog_post", result.clone());
        test_wasm.set_chain(chain);
        
        let posts: Vec<String> = test_wasm
            .call("blog_zome", "get_all_posts", "{}")
            .unwrap();
        
        assert!(posts.contains(&result));
    }

    // 测试获取博客文章
    #[test]
    fn test_get_blog_post() {
        let mut test_wasm = TestWasm::from_file("path/to/blog_zome.wasm");
        
        // 预先设置测试数据
        let mut chain = TestChain::new();
        let post_hash = "QmTestPostHash123";
        chain.add_entry("blog_post", post_hash);
        test_wasm.set_chain(chain);
        
        // 调用获取文章函数
        let result: serde_json::Value = test_wasm
            .call("blog_zome", "get_post", json!({"hash": post_hash}).to_string())
            .unwrap();
        
        // 验证返回结果
        assert_eq!(result["hash"], post_hash);
    }

    // 测试错误处理 - 无效输入
    #[test]
    fn test_invalid_input_handling() {
        let test_wasm = TestWasm::from_file("path/to/blog_zome.wasm");
        
        // 测试空标题的情况
        let post_data = json!({
            "title": "",
            "content": "无效的博客文章",
            "category": "技术"
        });
        
        let result = test_wasm.call::<String>("blog_zome", "create_post", post_data.to_string());
        
        // 验证返回错误
        assert!(result.is_err());
        assert_eq!(result.unwrap_err().to_string(), "Title cannot be empty");
    }

    // 测试网络调用模拟
    #[test]
    fn test_network_simulation() {
        let mut test_wasm = TestWasm::from_file("path/to/blog_zome.wasm");
        
        // 设置模拟网络响应
        use holochain_wasm_test_utils::MockNetwork;
        let mut network = MockNetwork::new();
        network.mock_call(
            "remote_blog_zome", 
            "get_remote_posts", 
            r#"[{"title":"远程文章","hash":"remote_hash_1"}]"#
        );
        test_wasm.set_network(network);
        
        // 调用会触发网络请求的函数
        let result: Vec<serde_json::Value> = test_wasm
            .call("blog_zome", "sync_remote_posts", r#"{"since":"2023-01-01"}"#)
            .unwrap();
        
        // 验证模拟响应
        assert_eq!(result[0]["title"], "远程文章");
        assert_eq!(result[0]["hash"], "remote_hash_1");
    }
}

最佳实践

  1. 隔离测试:每个测试应该专注于一个功能点,避免测试间的相互依赖
  2. 使用模拟数据:充分利用库提供的模拟环境,而不是尝试模拟完整的Holochain运行时
  3. 测试边界条件:特别关注输入验证和错误处理路径
  4. 保持测试快速:WASM测试应该快速执行,避免复杂设置

注意事项

  • 确保你的WASM文件是为测试正确构建的(通常需要--target wasm32-unknown-unknown
  • 测试环境与真实Holochain运行时存在差异,集成测试仍然是必要的
  • 定期更新库版本以获取最新功能和修复

通过使用holochain_wasm_test_utils,你可以显著简化Holochain WASM智能合约的测试流程,提高开发效率和代码质量。

回到顶部