Rust区块链开发工具parachains-runtimes-test-utils的使用:Substrate测试框架与平行链运行时模拟

Rust区块链开发工具parachains-runtimes-test-utils的使用:Substrate测试框架与平行链运行时模拟

安装

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

cargo add parachains-runtimes-test-utils

或者将以下行添加到您的Cargo.toml中:

parachains-runtimes-test-utils = "23.0.0"

元数据

  • 版本: 23.0.0
  • 发布时间: 15天前
  • 许可证: Apache-2.0
  • 大小: 47.6 KiB
  • Rust版本: 2021 edition

示例代码

以下是一个使用parachains-runtimes-test-utils进行平行链运行时测试的完整示例:

use parachains_runtimes_test_utils::{
    AccountId, Balances, ExtBuilder, Runtime, System, TestExternalities,
};
use sp_runtime::traits::IdentifyAccount;

// 设置测试环境
fn setup_test_env() -> TestExternalities {
    ExtBuilder::default()
        .balances(vec![
            // 初始化账户余额
            (alice_account(), 100_000),
            (bob_account(), 50_000),
        ])
        .build()
}

// 获取测试账户
fn alice_account() -> AccountId {
    AccountId::from([1u8; 32])
}

fn bob_account() -> AccountId {
    AccountId::from([2u8; 32])
}

#[test]
fn test_balance_transfer() {
    let mut ext = setup_test_env();
    
    ext.execute_with(|| {
        // 初始余额检查
        assert_eq!(Balances::free_balance(&alice_account()), 100_000);
        assert_eq!(Balances::free_balance(&bob_account()), 50_000);
        
        // 执行转账
        Balances::transfer(
            RuntimeOrigin::signed(alice_account()),
            bob_account().into(),
            10_000,
        ).unwrap();
        
        // 转账后余额检查
        assert_eq!(Balances::free_balance(&alice_account()), 90_000);
        assert_eq!(Balances::free_balance(&bob_account()), 60_000);
    });
}

#[test]
fn test_block_production() {
    let mut ext = setup_test_env();
    
    ext.execute_with(|| {
        // 初始区块号检查
        assert_eq!(System::block_number(), 0);
        
        // 生产一个区块
        System::set_block_number(1);
        
        // 验证区块号已更新
        assert_eq!(System::block_number(), 1);
    });
}

功能说明

parachains-runtimes-test-utils提供以下主要功能:

  1. 测试环境构建器 (ExtBuilder) - 轻松设置具有自定义初始状态的测试环境
  2. 模拟区块生产 - 测试链上操作和状态转换
  3. 账户管理 - 创建和管理测试账户
  4. 余额操作 - 测试代币转移和其他金融操作
  5. 事件检查 - 验证运行时事件是否正确触发

这个工具集是Substrate框架生态系统中测试平行链运行时的重要组成部分,特别适合开发需要与中继链交互的平行链应用。

完整示例代码

以下是一个更完整的示例,展示了如何使用parachains-runtimes-test-utils进行全面的平行链运行时测试:

use parachains_runtimes_test_utils::{
    AccountId, Balances, ExtBuilder, Runtime, System, TestExternalities,
    Event, RuntimeCall, RuntimeEvent
};
use sp_runtime::traits::IdentifyAccount;
use frame_support::{assert_ok, assert_noop};

// 设置更复杂的测试环境
fn setup_complex_test_env() -> TestExternalities {
    ExtBuilder::default()
        .balances(vec![
            (alice_account(), 200_000),
            (bob_account(), 100_000),
            (charlie_account(), 50_000),
        ])
        .build()
}

// 测试账户
fn alice_account() -> AccountId {
    AccountId::from([1u8; 32])
}

fn bob_account() -> AccountId {
    AccountId::from([2u8; 32])
}

fn charlie_account() -> AccountId {
    AccountId::from([3u8; 32])
}

#[test]
fn test_complex_balance_operations() {
    let mut ext = setup_complex_test_env();
    
    ext.execute_with(|| {
        // 初始状态验证
        assert_eq!(System::block_number(), 0);
        assert_eq!(Balances::free_balance(&alice_account()), 200_000);
        assert_eq!(Balances::free_balance(&bob_account()), 100_000);
        assert_eq!(Balances::free_balance(&charlie_account()), 50_000);
        
        // Alice转账给Bob
        assert_ok!(Balances::transfer(
            RuntimeOrigin::signed(alice_account()),
            bob_account().into(),
            25_000,
        ));
        
        // Bob转账给Charlie
        assert_ok!(Balances::transfer(
            RuntimeOrigin::signed(bob_account()),
            charlie_account().into(),
            10_000,
        ));
        
        // 验证转账后的余额
        assert_eq!(Balances::free_balance(&alice_account()), 175_000);
        assert_eq!(Balances::free_balance(&bob_account()), 85_000);
        assert_eq!(Balances::free_balance(&charlie_account()), 60_000);
        
        // 测试无效转账(余额不足)
        assert_noop!(
            Balances::transfer(
                RuntimeOrigin::signed(charlie_account()),
                alice_account().into(),
                100_000,
            ),
            "Insufficient balance"
        );
    });
}

#[test]
fn test_block_and_event_processing() {
    let mut ext = setup_complex_test_env();
    
    ext.execute_with(|| {
        // 生产10个区块
        for i in 1..=10 {
            System::set_block_number(i);
            
            // 每个区块Alice都转账1给Bob
            assert_ok!(Balances::transfer(
                RuntimeOrigin::signed(alice_account()),
                bob_account().into(),
                1,
            ));
            
            // 验证事件是否触发
            let event = RuntimeEvent::Balances(
                pallet_balances::Event::Transfer {
                    from: alice_account(),
                    to: bob_account(),
                    amount: 1,
                }
            );
            assert!(System::events().iter().any(|record| record.event == event));
        }
        
        // 最终验证
        assert_eq!(System::block_number(), 10);
        assert_eq!(Balances::free_balance(&alice_account()), 200_000 - 10);
        assert_eq!(Balances::free_balance(&bob_account()), 100_000 + 10);
    });
}

1 回复

以下是基于您提供内容的完整示例demo,包含创建测试环境、模拟区块生产和XCM测试的完整集成示例:

// 1. 导入必要模块
use parachains_runtimes_test_utils::{
    AccountId, SudoCall, MockGenesisConfig, construct_extrinsic, TestExternalities,
    run_to_block, RelayChainCall, ParachainCall,
    XcmCall, XcmOrigin, xcm_sender, register_parachain, ParachainId
};
use frame_support::{assert_ok, assert_eq};
use frame_system::Origin;

// 2. 定义测试运行时
type TestRuntime = runtime::Runtime;

// 3. 创建测试环境
fn setup_test_env() -> TestExternalities {
    let mut t = frame_system::GenesisConfig::default()
        .build_storage::<TestRuntime>()
        .unwrap();
    
    // 初始化余额pallet
    pallet_balances::GenesisConfig::<TestRuntime> {
        balances: vec![(1, 1000), (2, 2000)],
    }
    .assimilate_storage(&mut t)
    .unwrap();
    
    MockGenesisConfig::assimilate_storage(&mut t).unwrap();
    
    TestExternalities::new(t)
}

// 4. 完整测试示例
#[test]
fn integrated_parachain_test() {
    // 初始化测试环境
    let mut test_env = setup_test_env();
    
    test_env.execute_with(|| {
        // 测试1: 模拟区块生产
        run_to_block(1); // 前进到区块1
        
        // 模拟中继链初始化区块
        let relay_call = RelayChainCall::initialize_block(1);
        assert_ok!(
            construct_extrinsic(relay_call)
                .dispatch(Origin::root())
        );
        
        // 模拟平行链调用
        let para_call = ParachainCall::transfer {
            to: 2,
            amount: 500,
        };
        assert_ok!(
            construct_extrinsic(para_call)
                .dispatch(Origin::signed(1))
        );
        
        // 验证状态变化
        assert_eq!(Balances::free_balance(&1), 500);
        assert_eq!(Balances::free_balance(&2), 2500);
        
        // 测试2: XCM消息测试
        run_to_block(2); // 前进到区块2
        
        // 注册目标平行链
        register_parachain(ParachainId::from(2000));
        
        // 发送XCM消息
        let xcm_message = XcmCall::ReserveAssetDeposit {
            assets: vec![(Here, 100u128).into()],
            dest: Parachain(2000).into(),
        };
        
        assert_ok!(
            xcm_sender::send_xcm::<TestXcmConfig>(
                XcmOrigin::from(Some(1)),
                xcm_message
            )
        );
        
        // 处理XCM消息
        run_to_block(3);
        
        // 验证XCM执行结果
        assert_eq!(Balances::free_balance(&1), 400);
        
        // 测试3: 多平行链交互
        register_parachain(ParachainId::from(2001));
        
        // 模拟跨平行链交互...
    });
}

// 5. 运行测试
fn main() {
    integrated_parachain_test();
    println!("All tests passed!");
}

这个完整示例包含以下关键部分:

  1. 环境初始化:创建带有自定义创世配置的测试环境
  2. 区块生产模拟:使用run_to_block模拟区块生成过程
  3. 中继链交互:通过RelayChainCall模拟中继链调用
  4. 平行链操作:执行平行链上的转账操作并验证状态
  5. XCM测试:完整流程包括注册平行链、发送XCM消息和验证结果
  6. 多链交互:演示如何注册多个平行链并准备跨链交互

所有测试都在独立的环境中运行,遵循了最佳实践,并包含了状态验证和错误处理。

回到顶部