熟练掌握 debug 和 test_scenario 这两个工具包,开发者在 Move 开发中遇到问题时,便能够高效地进行调试与模拟测试,极大提升开发效率与代码质量。
Move 当前不支持原生调试器。但可借助 std::debug
模块与测试工具包 test_scenario
进行有效的调试与模拟测试。通过这种方式监控变量的值和模拟测试环境进行交易和功能的验证,从而深入了解模块的逻辑。
引入std::debug
模块
use std::debug;
在需要打印变量值 v
的地方,可以使用以下代码,无论其类型为何:
// 打印变量值 v 的地方
debug::print(&v);
// 如果 v 已经是一个引用,则使用以下代码:
打印当前的堆栈跟踪
debug::print_stack_trace();
my_module
中使用 debug
调试代码创建一个新的package
sui move new my_first_package
这里用的测试源码是官网里面的案例/examples/move/my_first_package/example.move
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0
// docs::#first
module my_first_package::example;
use std::debug;
// Part 1: These imports are provided by default
// use sui::object::{Self, UID};
// use sui::transfer;
// use sui::tx_context::{Self, TxContext};
// Part 2: struct definitions
public struct Sword has key, store {
id: UID,
magic: u64,
strength: u64,
}
public struct Forge has key {
id: UID,
swords_created: u64,
}
// Part 3: Module initializer to be executed when this module is published
fun init(ctx: &mut TxContext) {
let admin = Forge {
id: object::new(ctx),
swords_created: 0,
};
// Transfer the forge object to the module/package publisher
transfer::transfer(admin, ctx.sender());
}
// Part 4: Accessors required to read the struct fields
public fun magic(self: &Sword): u64 {
self.magic
}
public fun strength(self: &Sword): u64 {
self.strength
}
public fun swords_created(self: &Forge): u64 {
self.swords_created
}
// Part 5: Public/entry functions (introduced later in the tutorial)
// docs::#first-pause
public fun sword_create(magic: u64, strength: u64, ctx: &mut TxContext): Sword {
// Create a sword
Sword {
id: object::new(ctx),
magic: magic,
strength: strength,
}
}
/// 构造函数
public fun new_sword(forge: &mut Forge, magic: u64, strength: u64, ctx: &mut TxContext): Sword {
debug::print(forge);
forge.swords_created = forge.swords_created + 1;
debug::print(forge);
debug::print_stack_trace();
Sword {
id: object::new(ctx),
magic: magic,
strength: strength,
}
}
// docs::#first-resume
// Part 6: Tests
// docs::#first-test
#[test]
fun test_sword_create() {
// Create a dummy TxContext for testing
let mut ctx = tx_context::dummy();
// Create a sword
let sword = Sword {
id: object::new(&mut ctx),
magic: 42,
strength: 7,
};
// Check if accessor functions return correct values
assert!(sword.magic() == 42 && sword.strength() == 7, 1);
// docs::/#first-test}
// docs::#test-dummy
// Create a dummy address and transfer the sword
let dummy_address = @0xCAFE;
transfer::public_transfer(sword, dummy_address);
// docs::/#test-dummy
}
#[test]
fun test_sword_transactions() {
use sui::test_scenario;
// Create test addresses representing users
let initial_owner = @0xCAFE;
let final_owner = @0xFACE;
// First transaction executed by initial owner to create the sword
let mut scenario = test_scenario::begin(initial_owner);
{
// Create the sword and transfer it to the initial owner
let sword = sword_create(42, 7, scenario.ctx());
transfer::public_transfer(sword, initial_owner);
};
// Second transaction executed by the initial sword owner
scenario.next_tx(initial_owner);
{
// Extract the sword owned by the initial owner
let sword = scenario.take_from_sender<Sword>();
// Transfer the sword to the final owner
transfer::public_transfer(sword, final_owner);
};
// Third transaction executed by the final sword owner
scenario.next_tx(final_owner);
{
// Extract the sword owned by the final owner
let sword = scenario.take_from_sender<Sword>();
// Verify that the sword has expected properties
assert!(sword.magic() == 42 && sword.strength() == 7, 1);
// Return the sword to the object pool (it cannot be simply "dropped")
scenario.return_to_sender(sword)
};
scenario.end();
}
#[test]
fun test_module_init() {
use sui::test_scenario;
// Create test addresses representing users
let admin = @0xAD;
let initial_owner = @0xCAFE;
// First transaction to emulate module initialization
let mut scenario = test_scenario::begin(admin);
{
init(scenario.ctx());
};
// Second transaction to check if the forge has been created
// and has initial value of zero swords created
scenario.next_tx(admin);
{
// Extract the Forge object
let forge = scenario.take_from_sender<Forge>();
// Verify number of created swords
assert!(forge.swords_created() == 0, 1);
// Return the Forge object to the object pool
scenario.return_to_sender(forge);
};
// Third transaction executed by admin to create the sword
scenario.next_tx(admin);
{
let mut forge = scenario.take_from_sender<Forge>();
// Create the sword and transfer it to the initial owner
let sword = forge.new_sword(42, 7, scenario.ctx());
transfer::public_transfer(sword, initial_owner);
scenario.return_to_sender(forge);
};
scenario.end();
}
// docs::/#first
sui move test --skip-fetch-latest-git-deps
PS D:\data\web3\move_course\first_package> sui move test --skip-fetch-latest-git-deps
INCLUDING DEPENDENCY Sui
INCLUDING DEPENDENCY MoveStdlib
BUILDING my_first_package
Running Move unit tests
[debug] 0x0::example::Forge {
id: 0x2::object::UID {
id: 0x2::object::ID {
bytes: @0x34401905bebdf8c04f3cd5f04f442a39372c8dc321c29edfb4f9cb30b23ab96
}
},
swords_created: 0
}
[debug] 0x0::example::Forge {
id: 0x2::object::UID {
id: 0x2::object::ID {
bytes: @0x34401905bebdf8c04f3cd5f04f442a39372c8dc321c29edfb4f9cb30b23ab96
}
},
swords_created: 1
}
Call Stack:
[0] 0000000000000000000000000000000000000000000000000000000000000000::example::test_module_init
Code:
[35] LdU64(7)
[36] MutBorrowLoc(3)
[37] Call(15)
> [38] Call(5)
[39] LdConst(0)
[40] CallGeneric(2)
[41] ImmBorrowLoc(3)
Locals:
[0] -
[1] -
[2] { { { 034401905bebdf8c04f3cd5f04f442a39372c8dc321c29edfb4f9cb30b23ab96 } }, 1 }
[3] { 2, { 00000000000000000000000000000000000000000000000000000000000000ad, [2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 0, 0, 0 } }
Operand Stack:
[ PASS ] my_first_package::example::test_module_init
[ PASS ] my_first_package::example::test_sword_create
[ PASS ] my_first_package::example::test_sword_transactions
Test result: OK. Total tests: 3; passed: 3; failed: 0
PS D:\data\web3\move_course\first_package>
test_scenario
模拟测试环境在 Move 中,use sui::test_scenario;
是引入 Sui 的测试工具包 test_scenario
的语句。通过这个工具包,你可以创建模拟的测试环境,用于测试交易、模块和函数的行为。test_scenario
提供了一些有用的功能,帮助开发者在不依赖真实网络的情况下模拟和验证 Move 代码的逻辑。
主要功能
Sword
对象在多个交易中的创建和转移流程#[test]
fun test_sword_transactions() {
use sui::test_scenario;
// 创建测试地址,表示两个用户
let initial_owner = @0xCAFE; // 初始所有者地址
let final_owner = @0xFACE; // 最终所有者地址
// 第一次交易,由初始所有者执行,创建一把sword剑
let mut scenario = test_scenario::begin(initial_owner);
{
// 创建一把剑并转移给初始所有者
// 创建一个魔力为 42,力量为 7 的剑
let sword = sword_create(42, 7, scenario.ctx());
// 转移剑给初始所有者
transfer::public_transfer(sword, initial_owner);
};
// 第二次交易,由初始所有者执行
// 切换到初始所有者的下一步交易
scenario.next_tx(initial_owner);
{
// 从发送者处提取剑
let sword = scenario.take_from_sender<Sword>();
// 转移剑给最终所有者
transfer::public_transfer(sword, final_owner);
};
// 第三次交易,由最终所有者执行
// 切换到最终所有者的下一步交易
scenario.next_tx(final_owner);
{
// 从最终所有者处提取剑
let sword = scenario.take_from_sender<Sword>();
// 验证剑的属性是否正确
// 检查剑的魔力值和力量值
assert!(sword.magic() == 42 && sword.strength() == 7, 1);
// 将剑归还到对象池(不能简单地丢弃)
scenario.return_to_sender(sword);
};
// 结束当前测试场景
scenario.end();
}
Forge
对象的创建与管理
#[test]
fun test_module_init() {
use sui::test_scenario;
// 创建测试地址,表示管理员和初始所有者
let admin = @0xAD; // 管理员地址
let initial_owner = @0xCAFE; // 初始所有者地址
// 第一次交易,模拟模块初始化
let mut scenario = test_scenario::begin(admin);
{
// 执行模块初始化
init(scenario.ctx());
};
// 第二次交易,检查是否创建了 forge,并且初始值为0
// 切换到管理员的下一步交易
scenario.next_tx(admin);
{
// 从发送者提取 Forge 对象
let forge = scenario.take_from_sender<Forge>();
// 验证创建的剑数是否为0
// 验证初始时 forge 的 swords_created 字段是否为0
assert!(forge.swords_created() == 0, 1);
// 将 Forge 对象归还到对象池
scenario.return_to_sender(forge);
};
// 第三次交易,由管理员执行,创建一把剑
// 切换到管理员的下一步交易
scenario.next_tx(admin);
{
let mut forge = scenario.take_from_sender<Forge>();
// 创建一把剑并转移给初始所有者
// 创建一个魔力为 42,力量为 7 的剑
let sword = forge.new_sword(42, 7, scenario.ctx());
// 转移剑给初始所有者
transfer::public_transfer(sword, initial_owner);
// 将 forge 对象归还
scenario.return_to_sender(forge);
};
// 结束测试场景
scenario.end();
}
熟练掌握 debug
和 test_scenario
这两个工具包,开发者在 Move 开发中遇到问题时,便能够高效地进行调试与模拟测试,极大提升开发效率与代码质量。
Debugging <!--StartFragment-->
关注《HOH水分子》公众号,我们将持续分享和制作变成语言教程,让大家对编程产生化学反应。
<!--EndFragment-->
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!