在本课中,我们将编写一些有趣的测试。 首先,我们要更新之前编写的合约的初始化过程,因为我们引入了一个新变量,它应该存储在 c4 存储器中。 9 - Cell下溢。从片断基元读取数据时,试图读取的位或引用数超过总数。 更新我们的合约包装器首先,让我们更新我们的合约包装器,以便它能意识到 c4 存储中的第三个参数。也就是说,我们将更新 MainContractConfig 和 mainContractConfigToCell 函数 - // ...library imports/ H2 Q, f' P2 R, [7 k
- * \# n8 Q, `; [/ c3 Y
- export type MainContractConfig = {
2 D! u i9 \: |1 n$ m9 @ - number: number;
( ]+ i3 O. [% _/ [6 \1 J: n - address: Address;* {- V4 I" R+ s- d% [- P2 \" I6 ]
- owner_address: Address;
l. @# F" N! X* G/ Z% T - };
/ |- L0 \0 W. c. r( H" t; p; s0 ?: ] - $ X0 E3 Z1 W: C9 M: C- a$ i
- export function mainContractConfigToCell(config: MainContractConfig): Cell {0 P& l% _/ o2 Z6 |- Z7 _1 {
- return beginCell()
+ T; X& @2 L; e( M. F - .storeUint(config.number, 32)
& Y7 O- G! @6 R7 Q, U8 ^ - .storeAddress(config.address)
6 K% ~( J5 k( j) f+ } o) j - .storeAddress(config.owner_address)
% N4 x1 F' d! N) C: G - .endCell();. P( h; N$ e _8 ~' q( [% _7 \
- }) ]5 {, Q* [& ^0 ^) X
4 d" a$ n# B0 i/ ?- // ...contact wrapper class
复制代码封装文件中还有一点需要更新-getData 方法,这样它也会返回 owner_address 我们还将创建新方法 getBalance,以测试存款后余额是否增加:
5 `' n% O/ \$ S6 B1 {+ ]- async getData(provider: ContractProvider) {
$ p" Q7 V* K* L$ Y7 j, z8 `: g. ?# n - const { stack } = await provider.get("get_contract_storage_data", []);
4 p3 @' e! C0 z* f4 z5 Z - return {" E7 j' M, i8 X% u, @
- number: stack.readNumber(),0 @% p& ~' H7 F X
- recent_sender: stack.readAddress(),
. }# }! T/ R# w3 P+ g - owner_address: stack.readAddress(),
. j5 |0 ^ c4 U/ V0 T' N - };
/ e6 a1 g( X) ^3 J3 [: B/ m t3 { - }
- D5 m" k" e* f( F1 T2 L - ; ^4 k' i9 m) ]
- async getBalance(provider: ContractProvider) {
( H7 u: y @( W6 k1 r - const { stack } = await provider.get("balance", []);
- D! Y1 g7 o/ B% W+ |6 { - return {
1 r% s4 q) r2 C" W - number: stack.readNumber(),; A l3 @" f0 B# h
- };
2 a0 A( S# [ W; Z* r Y9 S - }
复制代码- // ...library imports
7 H6 @4 }: g7 g4 \: J5 J - , E& i" {4 ]% h+ w' K: q& j
- describe("main.fc contract tests", () => {
I4 ]: X/ y- H; Y - it("should get the proper most recent sender address", async () => {2 [5 z0 s0 m- e0 x# p2 {4 x
- const blockchain = await Blockchain.create();
% ]3 O6 w- F6 d: m3 j - const codeCell = Cell.fromBoc(Buffer.from(hex, "hex"))[0];
+ r: P6 M& R4 A) e& `5 Z8 }, L" Z - 7 q; j% ~+ n+ H# [
- const initAddress = await blockchain.treasury("initAddress");
$ j8 v: ^! K- F c - const ownerAddress = await blockchain.treasury("ownerAddress");3 q& e0 k t* i+ E$ D/ Q0 Y
- 4 h0 X: o" V) C' s1 s1 W5 p" }3 W
- const myContract = blockchain.openContract(
* L3 f: U2 R; e( z: c" G( Q - await MainContract.createFromConfig(1 ^; X$ {' ^- v& {0 }1 H2 q2 Y
- {9 ~& Q' ?- ]4 K/ G
- number: 0,4 l, n) f2 v5 ~
- address: initAddress.address,
* U) H. S, _! I) s: k. ]6 V& r - owner_address: ownerAddress.address, // now we create myContract from 3 parameters! Y2 g: A6 ^9 `
- },0 w% {0 E/ H1 [- l4 W/ v7 ?
- codeCell2 K) m2 Y8 G+ ~+ ?& U7 ?
- )
& B. P- d0 H7 m0 N - );" V- q" ^( y- ?7 J3 _* T* m, q
/ Q2 [& u2 b) w- // ...rest of testing code
复制代码
$ U6 d. X' g/ i2 g) @# T% W. @' j我们引入一个新的金库,它实际上将是合约的所有者,只有这个所有者以后才能提取资金。这些更新在 tests/main.spec.ts 中进行: - // ...library imports/ W, O% [# Y' E' y. l
- " k Y) o( |, n2 L' H2 ~
- describe("main.fc contract tests", () => {0 F) R7 g# I# U4 O
- it("should get the proper most recent sender address", async () => {6 X' L- y" k- p$ T$ m
- const blockchain = await Blockchain.create();. ]0 Q, b& ~ Z( t, J
- const codeCell = Cell.fromBoc(Buffer.from(hex, "hex"))[0];
4 j1 C) u! f- o# o6 [( k" z
W* L2 \0 K' b( |" H- const initAddress = await blockchain.treasury("initAddress");
+ a2 \+ C- ~1 r; w: e- \ - const ownerAddress = await blockchain.treasury("ownerAddress");
5 B2 `# {2 h4 i5 }5 F% N G - # g& x; q( {; Z) S$ q
- const myContract = blockchain.openContract(
( e. B% @/ q0 _, I - await MainContract.createFromConfig(, K$ Y# c& P1 `; O
- {) q |% V! v" x( E" b @
- number: 0,9 v' l; U! ?" {8 q! `* f
- address: initAddress.address,
p; d9 S) U. Y - owner_address: ownerAddress.address, // now we create myContract from 3 parameters
) q8 H! Q/ L, a" |" h v. `6 ~ - },4 m# V) R. N" D0 [" Z
- codeCell; L2 H. y" P0 a4 I5 `+ v+ A
- )
, C6 }) [& n9 J. I# W0 I5 ~( P - );& l, O6 S# E+ O5 I2 i3 }7 q2 Q
- 1 c: C, e$ j1 A, f, I: c
- // ...rest of testing code
复制代码现在我们好了。 yarn test 成功通过我们之前编写的测试。 ! `& F; I( F. J8 O% K4 D. Y( J
存款测试让我们在 main.spec.ts 文件中再添加几个测试。 由于我们必须在每次新测试前重新启动合约,因此我们还将把合约启动引入 Jest 的 beforeEach 函数: - // We need to additionally import SandboxContract and TreasuryContract( P/ h; ~ Y1 y! Y. F5 F* _
- import { Blockchain, SandboxContract, TreasuryContract } from "@ton/sandbox";
2 ?. g( P- B) N# x/ V% i/ M' S - // ... other library imports. ^% |3 [5 {$ I; _
- describe("main.fc contract tests", () => {2 x7 ], E# X u$ k; o1 S* t/ R
- let blockchain: Blockchain;" Z, E' ?6 q3 x7 t, W
- let myContract: SandboxContract<MainContract>;% q. |" A2 o& y# p" o5 g7 X
- let initWallet: SandboxContract<TreasuryContract>;( S- I( l+ X3 Z9 D4 a
- let ownerWallet: SandboxContract<TreasuryContract>;
^, \# }* R: Z6 y: g2 j4 f; n6 v - 4 C# {- q: J, k2 V4 E! C
- beforeEach(async () => {+ }/ }- d6 L# v
- blockchain = await Blockchain.create();
: |1 @* R; s: J1 {7 y" ? - initWallet = await blockchain.treasury("initWallet");2 w" k. o; |( v' d, Y/ r. K
- ownerWallet = await blockchain.treasury("ownerWallet");
$ ~* S! F* B8 K% {4 x - % ?# R' p1 X X: y
- const codeCell = Cell.fromBoc(Buffer.from(hex, "hex"))[0];
* ~" L4 H2 z$ h2 v8 q& j# V4 o
4 f& T+ c9 {) P6 B- myContract = blockchain.openContract(+ n2 s6 E) A2 t
- await MainContract.createFromConfig(
- E( j, @! A* n, Q; z% l% }8 Z$ d - {# `5 _ O8 ]2 `& T4 n
- number: 0,
, F0 D% D5 e* V& X1 Z - address: initWallet.address,
' R0 p8 B. O! m7 C4 y - owner_address: ownerWallet.address,: O) o# o/ k/ v# e
- },
: G* U8 m9 }/ S! P0 O - codeCell
1 k5 ^4 E4 V; c; O$ ]9 |' }6 r0 F - )
7 s, Z& z0 X7 q$ p - );7 h- r9 F+ i, A' I: {
- });
# q4 H: |5 ~0 s; d, T' e( H - # }3 w+ M; t6 l& v, @
- it("should get the proper most recent sender address", async () => {; ~9 X8 a( U6 E
- const senderWallet = await blockchain.treasury("sender");; G: Y. H4 V! F. L# m
- & _. f+ ]- x7 J
- const sentMessageResult = await myContract.sendIncrement(
. \0 d% Q6 f. L1 A" r) ~0 b - senderWallet.getSender(),: G( w% p7 Q4 n
- toNano("0.05"),
+ u, H" L0 r7 o4 M# I; s - 1
8 N" V4 m9 {# U. n - );
2 t) ~- J9 }/ l' q4 t: y - 5 X$ k0 c# n# I
- expect(sentMessageResult.transactions).toHaveTransaction({
" N$ [7 ~9 E ^3 k1 e - from: senderWallet.address,
7 S+ o. e% M; t/ {. ]# V, _8 { - to: myContract.address,
6 M% q/ Q# X1 d6 _- |1 D - success: true,2 H6 Z' J8 l% s: K
- });
5 Q9 O/ t$ f, r9 Z+ Y, e - 9 r- m6 F$ v0 z( r% i9 W
- const data = await myContract.getData();8 s5 |" E( ?& L) V3 b, b/ b
2 l& p2 Z4 Y- v$ R3 l- expect(data.recent_sender.toString()).toBe(senderWallet.address.toString());
$ T& R0 g5 H% }5 O4 k' H - expect(data.number).toEqual(1);8 u/ X- \) t$ V8 f
- });
) y% c1 x# H+ Z; ]; X - it("successfully deposits funds", () => {2 H% ]' ~: \& t* p4 s
- // test logic is coming
- e m) {4 g N% p: Z - });
6 u2 B" e0 ^. L5 D# X G - it("should return deposit funds as no command is sent", () => {/ j# v7 k. U1 I8 Q& \2 E
- // test logic is coming
; N& b( C9 ?0 Y& w, D# K - });, z4 j! C" ]. R( E5 C
- it("successfully withdraws funds on behalf of owner", () => {
% G/ T2 k7 E( l( a# r - // test logic is coming, U5 m/ ^% j* |% i5 W. G8 Q# O' k/ B
- });8 p' N3 P$ U# R, X
- it("fails to withdraw funds on behalf of non-owner", () => {8 M6 B& g8 v, o0 K) F: {
- // test logic is coming3 ]" w# F8 n9 f1 m& j+ |
- });
) J5 E0 M/ E3 d0 V `" ], b - it("fails to withdraw funds because lack of balance", () => {
( @; Y/ P5 o. K$ {1 h. B* @& s - // test logic is coming* W' k" H- c9 @: h. L
- });3 o! w9 }; p) X) U- J- W. ?
- });
复制代码我们只是将第一个测试的部分行移动到了 beforeEach 中,其余的行应留在第一个测试中 it
让我们为存款写一个测试。我们将发送一条 op == 2 的信息,然后检查余额。像往常一样,首先创建一个封装方法,我们将其称为 sendDeposit: - async sendDeposit(provider: ContractProvider, sender: Sender, value: bigint) {
6 H1 Y& \& T. P v - const msg_body = beginCell()
3 D: |8 q/ _; B( D/ \4 D - .storeUint(2, 32) // OP code. U% c* C$ z; ~% D* M( \7 U
- .endCell();' ^4 ?) ~- ^ T2 N1 {
- , z. c1 P Z1 w; h
- await provider.internal(sender, {
5 Z/ p/ B* n [ s; S9 p - value,
, z' z* n& p. C# w- T - sendMode: SendMode.PAY_GAS_SEPARATELY,
: J9 t1 ?5 p% C5 f- q - body: msg_body,, l: d, j" S( N, W8 i S
- });
; V8 N. N! I; @# S. f1 U - }
复制代码我们将不详细介绍这种方法,因为它非常简单--我们只需在信息正文中传递一个操作码。 下面是我们如何使用该方法运行存款测试。 - it("successfully deposits funds", async () => {
8 [: f1 B: D I, }! E8 K7 Y - const senderWallet = await blockchain.treasury("sender");
( f2 F) i0 g, |3 r" l( t- Q - . [9 o1 A# A% [- l m7 r) ` X
- const depositMessageResult = await myContract.sendDeposit(1 ?* B- T% @; w" E# y
- senderWallet.getSender(),
0 { I- J( a! T5 ^3 e$ y - toNano("5")) k/ I3 K- m$ V1 |# X
- );; A+ m7 D3 f: ]3 E+ e9 ~
- 7 S& E( j' ~/ r' J5 p2 E
- expect(depositMessageResult.transactions).toHaveTransaction({
4 F0 b; {, F3 M2 m; q! [* K - from: senderWallet.address,& f" a& Z; w/ B1 a' {# e# R$ ^
- to: myContract.address,3 p* Z8 i% C. d @
- success: true,
3 a" q& q4 `8 M2 {7 F - });& i) d: u; t1 o& ^4 t" y: M
; X( [8 v z3 z- const balanceRequest = await myContract.getBalance();
5 r5 N4 J5 \9 q7 B" S( ]( ]8 M5 |
; X P$ N4 |& @4 t$ i$ {7 l4 K- expect(balanceRequest.number).toBeGreaterThan(toNano("4.99"));
) a" S/ _# {5 I - });
复制代码请注意我们是如何检查合约余额是否大于 4.99 TON的,因为我们知道手续费会损失一些资金。 我们再写一个存款测试,但这次会出现误报。我们将在没有存款操作码的情况下发送资金,并期待返回交易,因为该操作码未知。 和以往一样,我们还有一个包装器: - async sendNoCodeDeposit(1 t% s: k3 n9 c/ X
- provider: ContractProvider,: V6 y) L1 N, s4 q! S
- sender: Sender,( t! R! |6 u, O! o4 J
- value: bigint
6 q+ y1 ^7 {5 b- g - ) {
0 D5 v% w) ^, I7 c - const msg_body = beginCell().endCell();
3 e6 Y7 N3 p3 P) L9 F% L* [
* h# c2 i" @. O, ]: ^- await provider.internal(sender, {1 Y2 ?5 j' ^6 I( }" B# E/ R
- value,% @$ f# L+ J- y* F
- sendMode: SendMode.PAY_GAS_SEPARATELY,
( h/ Q3 W7 c5 W6 p" W - body: msg_body,7 d; J8 `1 \" D) ~
- });$ L4 z( `4 v& E+ q4 R! f
- }
复制代码下面是测试代码: 0 \6 {) Q. ] f
- it("should return funds as no command is sent", async () => {" {/ B/ X& }8 U) e O0 x; U) ^
- const senderWallet = await blockchain.treasury("sender");, \# q5 U9 l/ `9 g
- * q1 G0 G5 V$ M6 `! H4 U8 T
- const depositMessageResult = await myContract.sendNoCodeDeposit(4 n* W$ w8 p& Y: Y
- senderWallet.getSender(),5 B/ p& ~1 Z# b
- toNano("5")6 P5 @4 w9 F& k) f+ O
- );9 ?' e2 p) Z0 |6 |) l) J
- ]6 w5 j9 ]" @
- expect(depositMessageResult.transactions).toHaveTransaction({( Z! @% t6 o0 |1 x0 J0 g
- from: myContract.address,8 B9 ^1 a: l" g
- to: senderWallet.address,
7 @: l% I( V% @ i9 l A - success: true,
: F/ n& _2 @+ b9 | x- Q' Z - });, o, [3 S+ ?; u: W5 {7 i
4 X4 D3 j5 l; f4 b- const balanceRequest = await myContract.getBalance();, Q/ T6 P' F; C
# J+ U+ x8 H8 d6 Y) w- expect(balanceRequest.number).toBe(0);
& @8 V# H: Y# l' m# p: V `# I - });
复制代码 提款测试让我们先创建一个用于提款的新包装器: - async sendWithdrawalRequest(
$ I$ @8 X) U' z - provider: ContractProvider, h7 U' H" o7 ~9 Q3 e5 @5 `3 n
- sender: Sender,
; @" R9 x+ J6 i& B7 r1 \1 [ - value: bigint,1 q. m0 U3 ~" q \' R! A
- amount: bigint
' }! X- f* P& |; `8 P - ) {+ k) ]" I, ?2 e x' P1 h
- const msg_body = beginCell()4 y/ ?5 Z' X* m! D
- .storeUint(3, 32) // OP code
7 }8 S2 W3 ~( P* w6 | - .storeCoins(amount)* D% X1 L5 y W
- .endCell();0 A, G; U/ B8 n
- 1 Q- z' o4 Z5 m) e! M
- await provider.internal(sender, {% L9 N) y7 q( x4 \7 d# K
- value,
* C) d6 g+ n' d3 S - sendMode: SendMode.PAY_GAS_SEPARATELY,
# U8 B5 F& z+ _" g, z - body: msg_body,
/ {+ n$ I1 g3 ]- I - });
9 m, f/ J. @" g1 ~# ? - }
复制代码我们输入适当的 msg_body 和所需的提款 amount 。 我们将进行 3 项不同的测试。让我们一起来看看: - it("successfully withdraws funds on behalf of owner", async () => {8 y, |& R- d/ y1 h( _ ~9 [3 F1 n
- const senderWallet = await blockchain.treasury("sender");0 b. O7 z4 p( |% R
$ P I6 g9 Y- [8 T; N- await myContract.sendDeposit(senderWallet.getSender(), toNano("5"));
' k- I2 Q! E+ b3 D0 c
5 W, ?: ?6 {* ~/ K: C4 u- const withdrawalRequestResult = await myContract.sendWithdrawalRequest(
( D6 [" O% j4 K) z - ownerWallet.getSender(),; e) c! d% R& ?
- toNano("0.05"),
) V& ^$ K+ p5 x) w - toNano("1"). ] O. ~' g( [: k7 S
- );* T$ w2 i$ v: }; m. y8 d- M
- 0 I- X, `5 d1 ]0 v/ {
- expect(withdrawalRequestResult.transactions).toHaveTransaction({1 Z& O% ^' b, P7 Y0 l( l1 M( ?
- from: myContract.address,
; r/ p7 E; J% u4 e( v - to: ownerWallet.address,
% K7 a, `$ z# i - success: true,
$ p' ?4 q L3 y* d$ p- w0 a) C k# \, L - value: toNano(1),
P* L- Q' ]! D - });
5 J0 d& P) a/ X( P& O- } - });
复制代码 要成功提款,我们首先要正确存款。然后我们调用 sendWithdrawalRequest 指定我们要提取 1 Ton。请注意,0.05 Ton只是为了支付费用而指定的信息值。% ]! \1 N) {7 d2 ^* I* m9 t
- it("fails to withdraw funds on behalf of not-owner", async () => {
4 z; L0 S( S# P/ M - const senderWallet = await blockchain.treasury("sender");, l- F/ N% X# x' D
2 n2 ] } ?, T& d1 G7 s3 S3 \- await myContract.sendDeposit(senderWallet.getSender(), toNano("5"));
9 t+ I9 P/ v+ E" M$ ^5 C
& a$ c7 X) W" G- const withdrawalRequestResult = await myContract.sendWithdrawalRequest(: O$ S; D+ ~' Z4 n' e7 s" s
- senderWallet.getSender(),
! k6 l z* d/ w* P9 a/ I3 o- l3 ^/ `- r - toNano("0.5"),- ^% \5 Y( j o- v3 V
- toNano("1")
: \( Z: q4 V9 Q/ u* I& y+ ~ - );
* R* U2 a+ T d, k5 s" J: |
. ^5 W5 T) q/ m8 t- M7 f5 {- expect(withdrawalRequestResult.transactions).toHaveTransaction({: g, H# c4 z3 D) H8 s& Q- }+ v
- from: senderWallet.address,
7 j% |, S: n" ? - to: myContract.address,
# I, z$ B0 X5 K* z - success: false,1 V( y8 h* K. M# F. `" w
- exitCode: 103,
7 W/ |& k+ p# @- l, J - });" ^ o- N3 O, ]: m* p
- });
复制代码在本次测试中,我们代表发送方钱包而不是所有者钱包发送提款请求(两者的区别在于初始化时我们提供的助记词). 还请注意,我们是如何通过 success: false 和具体的失败原因 exitCode: 103. 为了确保我们做得正确,请看一下我们的合约代码,我们有这样一行代码: - throw_unless(103, equal_slice_bits(sender_address, owner_address));
' z* K. k7 y! X8 X
复制代码我们进行最后一项测试--由于没有余额,提款失败: - it("fails to withdraw funds because lack of balance", async () => {
6 c& `8 @8 C5 R/ n& | - const withdrawalRequestResult = await myContract.sendWithdrawalRequest(: L# C! J7 u$ ^# @7 E" v
- ownerWallet.getSender(),; Z4 }/ T) x4 J$ V+ a/ B
- toNano("0.5"),, L4 f$ _3 i7 V) l* }
- toNano("1")
' [" y, b; z& `% D v5 l! N8 Y - );
2 [* o4 H7 ?3 |7 M3 v( x4 r - * R& W0 L' M5 n' ?1 C& @
- expect(withdrawalRequestResult.transactions).toHaveTransaction({# L" V! e' m1 \& f }* y
- from: ownerWallet.address,$ s c& s* o- G8 `
- to: myContract.address,
3 Z+ m! p3 l! ?' \. {! g. }3 ?7 J - success: false,
% a# C7 q7 V0 r U0 U - exitCode: 104,
5 [2 w6 P1 z B; g - });
) Y% p5 B7 D- y - });
复制代码- throw_unless(104, balance >= withdraw_amount);# _: [4 R7 _( r! \6 A3 U- E3 w9 m
复制代码就是这样 几章之后,我们将学习如何构建网络客户端,以便与合约进行交互。
; {5 M& ]1 m M( j- C7 a2 S2 |% t6 y Q: d$ l+ i) ~4 F) n( f: l
|