主要讲一下如何使用test_scenario 进行复杂的测试,辅以一些例子尽量让朋友们掌握并且可以运用到项目当中。
主要会分为两部分来讲解:
- Test Annotation主要讲一下关于测试注释的基本用法和一些个小例子。
- Test Scenario主要讲一下如何使用test_scenario 进行复杂的测试,辅以一些例子尽量让朋友们掌握并且可以运用到项目当中。
Intro to Test Scenario
test_scenario Module 提供了一个可以模拟多用户,多Transaction的场景,我们需要理解并与之交互的主要是Scenario对象。
public struct Scenario {
txn_number: u64,
ctx: TxContext,
}
Scenario 对象维护了一个全局对象的池子,可以简单的理解所有在测试过程中的对象都会存储在这个池子中,因此这些对象可以通过像 `take_from_sender` 这样的函数来访问。下面介绍使用test_scenario需要掌握的知识点。
public fun begin(sender: address): Scenario
public fun end(scenario: Scenario): TransactionEffects
// 初始化一个虚拟的交易地址,名称是addr,地址是@0xA
let addr = @0xA;
// 以addr作为发送方开始模拟多Transaction的Scenario,返回Scenario对象
let mut scenario = test_scenario::begin(addr1);
...
// 清除掉scenario对象
test_scenario::end(scenario);
public fun next_tx(scenario: &mut Scenario, sender: address): TransactionEffects
将Scenario推进到一个新的Transaction中,其中“sender”是交易发送者,所有转移的对象都会被移入到全局对象池中。换句话说,为了使用各种“take”方法来访问对象例如take_from_address_by_id
,Transaction必须首先执行 next_tx
方法来获取上一笔Transaction的结果(TransactionEffects
)。
注意!! 如果共享或不可变对象被删除、传输或包装,或者无法返回 TransactionEffects 程序将中止。
public struct TransactionEffects has drop {
/// The objects created this transaction
created: vector<ID>,
/// The objects written/modified this transaction
written: vector<ID>,
/// The objects deleted this transaction
deleted: vector<ID>,
/// The objects transferred to an account this transaction
transferred_to_account: VecMap<ID, /* owner */ address>,
/// The objects transferred to an object this transaction
transferred_to_object: VecMap<ID, /* owner */ ID>,
/// The objects shared this transaction
shared: vector<ID>,
/// The objects frozen this transaction
frozen: vector<ID>,
/// The number of user events emitted this transaction
num_user_events: u64,
}
我们来看一个简单的例子,
分别为Sword对象的两个属性定义Get方法。
public struct Sword has key, store{
id:UID,
magic: u64,
strength:u64,
}
public fun sword_create(magic:u64,strength:u64,ctx: & mut TxContext):Sword{
let sword = Sword {
id:object::new(ctx),
magic:magic,
strength:strength,
};
sword
}
public fun magic(sword:&Sword):u64 {
sword.magic
}
public fun strength(sword:&Sword):u64 {
sword.strength
}
下面是Scenario测试代码:
#[test]
fun test_sword_transactions() {
use sui::test_scenario;
// 创建两个变量代表两个不一样的地址
let initial_owner = @0xCAFE;
let final_owner = @0xFACE;
// 使用initial_owner来开启test_scenario,也是测试的第一个tx;
let mut scenario = test_scenario::begin(initial_owner);
{
// 创建Sword对象,并transfer给initial_owner;
let sword = sword_create(42, 7, scenario.ctx());
transfer::public_transfer(sword, initial_owner);
};
// 第二个tx同样也由initial_owner来启动
scenario.next_tx(initial_owner);
{
// 把Sword从initial_owner的地址中拿出来,并transfer给final_owner;
let sword = scenario.take_from_sender<Sword>();
transfer::public_transfer(sword, final_owner);
};
// 第三个tx由final_owner来启动
scenario.next_tx(final_owner);
{
// 把Sword从final_owner的地址中拿出来
let sword = scenario.take_from_sender<Sword>();
// 这里做一下属性验证看是否和之前创建的时候一致
assert!(sword.magic() == 42 && sword.strength() == 7, 1);
// 因为我们的Sword没有Drop能力,所以这里我们需要还回去,或者transfer给其他地址
scenario.return_to_sender(sword)
};
scenario.end();
}
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!