测试
现在,你可以学习如何使用 Rust 编程语言和 gtest
库为智能合约程序编写测试。
本课程将指导你测试一个托管智能合约程序,涵盖以下方面:
- 初始化合约
- 从买家账户存款
- 检查正确的合约执行和失败情况 让我们来测试我们的方法。
首先,我们将创建 tests
目录和 escrow_test.rs
文件:
mkdir tests
cd tests
touch escrow_test.rs
然后,我们将从 gtest
库和 escrow 包中导入必要的结构,并为买家、卖家和产品价格定义常量。
接下来,我们将使用以下代码发送初始化消息:
tests/escrow_test.rs
use escrow_io::{InitEscrow, EscrowAction, EscrowEvent};
use gtest::{Log, Program, System};
const BUYER: u64 = 100;
const SELLER: u64 = 101;
const PRICE: u128 = 100_000;
#[test]
fn deposit() {
let sys = System::new();
sys.init_logger();
let escrow = Program::current(&sys);
let res = escrow.send(
SELLER,
InitEscrow {
seller: SELLER.into(),
buyer: BUYER.into(),
price: PRICE,
},
);
assert!(!res.main_failed());
}
接下来,我们将使用Program::send_with_value
function instead of Program::send
函数而不是 Program::send
函数从买家的账户发送消息,因为我们需要发送带有资金的消息。然而,在测试节点中,账户余额为零,所以我们将进行修改:
tests/escrow_test.rs
sys.mint_to(BUYER, PRICE);
let res = escrow.send_with_value(
BUYER,
EscrowAction::Deposit,
PRICE,
);
let log = Log::builder()
.dest(BUYER)
.payload(EscrowEvent::FundsDeposited);
assert!(res.contains(&log));
为了保持事情井然有序,让我们将合约初始化移入名为 init_escrow()
的单独函数中:
tests/escrow_test.rs
fn init_escrow(sys: &System) {
sys.init_logger();
let escrow = Program::current(&sys);
let res = escrow.send(
SELLER,
InitEscrow {
seller: SELLER.into(),
buyer: BUYER.into(),
price: PRICE,
},
);
assert!(!res.main_failed());
}
我们可以利用 gtest
库提供的 System::get_program
函数来在测试函数内获取程序。
正如我们在第一课中讨论的那样,我们使用第一个 ID 初始化我们的程序。因此,存款测试函数的完整代码如下:
tests/escrow_test.rs
const ESCROW_ID: u64 = 1;
#[test]
fn deposit() {
let sys = System::new();
init_escrow(&sys);
let escrow = sys.get_program(ESCROW_ID);
sys.mint_to(BUYER, PRICE);
let res = escrow.send_with_value(
BUYER,
EscrowAction::Deposit,
PRICE,
);
let log = Log::builder()
.dest(BUYER)
.payload(EscrowEvent::FundsDeposited);
assert!(res.contains(&log));
let escrow_balance = sys.balance_of(ESCROW_ID);
assert_eq!(escrow_balance, PRICE);
}
在测试结束时,我们还将使用 System::balance_of
函数向程序验证资金的贷记。
测试正确的合约执行和失败情况是至关重要的。我们必须确认合约是否会出现以下情况:
- 消息是从错误的账户发送的;
- 买家附加不足的资金;
- 托管状态不是
AwaitingPayment
。
让我们在 deposit
函数中测试所有这些情况:
tests/escrow_test.rs
#[test]
fn deposit_failures() {
let sys = System::new();
init_escrow(&sys);
let escrow = sys.get_program(ESCROW_ID);
sys.mint_to(BUYER, 2*PRICE);
// must fail since BUYER attaches not enough value
let res = escrow.send_with_value(
BUYER,
EscrowAction::Deposit,
2*PRICE - 500,
);
assert!(res.main_failed());
// must fail since the message sender is not BUYER
let res = escrow.send(SELLER, EscrowAction::Deposit);
assert!(res.main_failed());
// successful deposit
let res = escrow.send_with_value(
BUYER,
EscrowAction::Deposit,
PRICE,
);
assert!(!res.main_failed());
// must fail since the state must be `AwaitingPayment`
let res = escrow.send_with_value(
BUYER,
EscrowAction::Deposit,
PRICE,
);
assert!(res.main_failed());
}
很好,我们已经编写了一半的程序。现在轮到你开始 coding 了。