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提供以下主要功能:
- 测试环境构建器 (ExtBuilder) - 轻松设置具有自定义初始状态的测试环境
- 模拟区块生产 - 测试链上操作和状态转换
- 账户管理 - 创建和管理测试账户
- 余额操作 - 测试代币转移和其他金融操作
- 事件检查 - 验证运行时事件是否正确触发
这个工具集是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!");
}
这个完整示例包含以下关键部分:
- 环境初始化:创建带有自定义创世配置的测试环境
- 区块生产模拟:使用
run_to_block
模拟区块生成过程 - 中继链交互:通过
RelayChainCall
模拟中继链调用 - 平行链操作:执行平行链上的转账操作并验证状态
- XCM测试:完整流程包括注册平行链、发送XCM消息和验证结果
- 多链交互:演示如何注册多个平行链并准备跨链交互
所有测试都在独立的环境中运行,遵循了最佳实践,并包含了状态验证和错误处理。