正如我之前提到的,编写测试将是 FunC 合约编程中非常重要的一部分。让我们回顾一下上一课创建的测试: 0 h* U) S( |4 M8 e0 E% Q
- import { Cell, toNano } from "ton-core";
* l- K* G- L+ S% R9 E% J - import { hex } from "../build/main.compiled.json";
% B3 z! z' L- o/ ]8 H - import { Blockchain } from "@ton-community/sandbox";
N0 q0 ^8 X9 c! L0 N8 V - import { MainContract } from "../wrappers/MainContract";
# d" d3 j9 q; N% V* O' X( u - import "@ton-community/test-utils";
$ @3 q" p$ k( I0 y) K% x - 5 \5 e# f5 @, c: G
- describe("main.fc contract tests", () => {; K" S( |. W$ E/ d
- it("should get the proper most recent sender address", async () => {
. o/ m e' z& t" `! P - const blockchain = await Blockchain.create();3 i4 y; F0 @. }- `2 B( H A! A
- const codeCell = Cell.fromBoc(Buffer.from(hex, "hex"))[0];
" v3 G* v* q* W% F- m, h. w e - # R w/ \% }2 z
- const myContract = blockchain.openContract(
" |: t7 D0 ^5 ^ - await MainContract.createFromConfig({}, codeCell)
# `3 {) }$ N* R- H4 N* } - );
; C/ [; w5 y: S' ]" b& H
, f K9 {8 s& E% f! G% N5 I: X% r' l- const senderWallet = await blockchain.treasury("sender");2 M' i% P7 M/ ^% S( I! H6 F' q, B
3 Y9 [/ `2 T, a- const sentMessageResult = await myContract.sendInternalMessage(& C9 S; h" t, W% G
- senderWallet.getSender(),
( {4 ]4 R& j0 b; M - toNano("0.05")
4 _' n( ^' g. f: ` - );/ ]# q2 U$ L, p. K, P+ y
& S+ U6 J# M& C7 J- expect(sentMessageResult.transactions).toHaveTransaction({, V2 h: U4 U- S3 ~3 B! W% \
- from: senderWallet.address,, I# V& ]" z, T# _$ E
- to: myContract.address,$ `7 [ U7 ^+ Q( m& @( d) b
- success: true,$ N5 S w6 S! ]9 l# j/ M: Z) g
- });
# z8 z7 }% O% J2 o: z# B- P - 0 f' s# Q4 U8 Q0 t+ h7 ?8 [) X) B7 W' ?
- const data = await myContract.getData();
: ^- O2 l& F8 @! v( v; t, ^ - ( U2 L9 }; ~9 w# s
- expect(data.recent_sender.toString()).toBe(senderWallet.address.toString());
! V, X! O0 Z0 ?( g - });3 Y# a; d0 W6 ], f' h+ p* v: j
- });
复制代码值得一提的是,如果我们现在运行 yarn 测试,它将无法工作,因为我们的代码发生了很大变化。为了解决这个问题,我们需要做的如下: 让我们来处理初始数据。正如你所记得的,我们使用 createFromConfig 方法来初始化我们的合约,并通过以下方式将合约的初始状态数据设置为简单的空Cell: - static createFromConfig(config: any, code: Cell, workchain = 0) {: d }# q' L1 Z1 B, i
- const data = beginCell().endCell();- o( e- M, V& `4 d
- const init = { code, data };/ T4 n+ F9 T$ S" R& U
- const address = contractAddress(workchain, init);4 ]6 i/ |& G" o2 _5 c% V
- * d& l- t0 c; q7 g
- return new MainContract(address, init);/ ?3 _: X n* ]% u( Y* B
- }
复制代码然后在 main.spec.ts 中调用 createFromConfig: % c* X1 W7 {4 p2 o
- const myContract = blockchain.openContract(
, u+ {+ r$ C. @ - await MainContract.createFromConfig({}, codeCell)" Y7 z& g" T1 @* h
- );
复制代码到目前为止,我们传递的是一个空对象作为配置,但现在我们可以传递一些值,这些值实际上是我们初始化合约的适当数据所需要的。 让我们定义一下我们的 config 应该是什么样子。在 wrappers/MainContract.ts 中导入后,我们将定义一个名为 MainContractConfig 的新类型。 - // ... library imports+ C4 W* \/ W3 o8 b$ u4 {
- v) x/ s" k$ Z8 Z2 q+ w _, J b- export type MainContractConfig = {
* M% |6 n9 W+ k: m) r - number: number;' Y/ W+ b4 k& L5 M
- address: Address;
2 Q, d3 }- |( I+ ?) e) D. _; k! h) O - };
复制代码 我们将在同一封装文件中实现的另一个很酷的辅助函数是 mainContractConfigToCell.它将接收 MainContractConfig 类型的对象,将这些参数正确打包到Cell中并返回.我们需要这样做,因为你还记得,c4 存储空间是一个存储Cell:% |; e9 J/ A8 V; r* O
- // ... library imports
8 @; s" K1 _( S$ y - 0 d" f& s5 X% s+ ~2 ]
- // ... MainContractConfig type( d5 t( H$ e, ^7 |' Q7 t( U! C- j
& S' T' {5 l; i6 X7 u; A! A- export function mainContractConfigToCell(config: MainContractConfig): Cell {
* y8 d* O5 I) c2 y* B - return beginCell().storeUint(config.number, 32).storeAddress(config.address).endCell();0 w, E7 {/ f$ S5 ?6 L
- }
复制代码 现在让我们更新同一文件中的 createFromConfig,不过是在 MainContract 类中。现在,它必须使用 mainContractConfigToCell 函数来形成初始状态数据 Cell:
" f2 v# K: m d! } H4 }- // ... library imports
' ~; |( H+ H) @% E' m7 O* y% T1 J - // ... MainContractConfig type; c0 s2 w8 g8 F; \6 D7 A
- // ... mainContractConfigToCell function
1 ?) p3 o9 \2 s( n
; ]1 A! h9 I6 ~& I6 l6 }- export class MainContract implements Contract {% T7 w F7 D) v3 n. q6 P$ u9 h
- constructor(; H8 m- _; e( p2 u: z6 D
- readonly address: Address,- g- S; Q1 R6 [
- readonly init?: { code: Cell; data: Cell }9 H- s6 T- g u1 ?" b* b+ B1 j9 l
- ) {}
! U4 A F2 g+ d0 e$ R. \, u - $ I5 R9 u2 T5 E1 F& \: b0 v; S; Z2 K
- static createFromConfig(" D! }5 z3 F0 b+ z- Q' W
- config: MainContractConfig,: j' h1 X, M1 Y3 O ?
- code: Cell,
7 \( J$ Q4 p' M4 r7 k% l - workchain = 0. a$ w$ |: j; B1 E; G6 L* y7 w! F
- ) {% O, o. t2 |0 z4 k: R" R
- const data = mainContractConfigToCell(config);' F/ k& m* M( Y% j
- const init = { code, data };
! u1 ^7 R1 X) [! F- g8 K0 A - const address = contractAddress(workchain, init);! |7 w$ t$ l6 {" i0 x8 T: a0 I8 |
4 O& A2 I. a; `$ s' ]- return new MainContract(address, init);
8 Y6 v3 t$ H& |. J - }
4 Z1 i% }1 c% B" ]4 F2 U - / \! D' M6 r' c% V& u
- // ... rest of the methods0 w3 E |$ r: v7 C: ^ [" F7 m
- }
复制代码现在,我们的 createFromConfig 方法已经准备就绪,让我们更新一下如何在 tests/main.spec.ts 文件中使用该方法:
, [( F# n. ~2 R6 z, p0 j- i- // ... library imports0 Q9 U+ b6 p: |* v7 Y0 c! i
- 3 R% f$ j/ B( f
- describe("main.fc contract tests", () => {5 Q8 G/ H# b. \( l2 a1 p4 p
- it("should get the proper most recent sender address", async () => {4 v$ l5 k3 I7 ]: ^) G6 i) c
- const blockchain = await Blockchain.create();( n5 R" J; i, L2 f5 J
- const codeCell = Cell.fromBoc(Buffer.from(hex, "hex"))[0];. ]+ @+ e! |& T6 `
- 2 C1 _& p6 B j
- const initAddress = await blockchain.treasury("initAddress");& H9 B8 w1 w. p" h$ ?3 @, h5 c
- / a9 I1 |0 w. K
- const myContract = blockchain.openContract(! `% a$ B+ U. y( w" U
- await MainContract.createFromConfig(6 b% t- |9 G+ c7 r
- {) O7 z8 S- ]( y/ E+ `4 ~
- number: 0,9 u) }. D0 Y/ R& I4 ~5 s* c
- address: initAddress.address,
* P- K9 T! s$ J i, z" J - },# w. H. U3 ]1 ~% ^9 t B' c
- codeCell
/ l$ d9 x+ @8 p O1 c% ` - )
" C3 p2 a; A% g2 ~ - );
7 A0 i% K: a8 y - ( k8 i' ] B: V5 @: j2 S7 n
- // ... the rest of old tests logic( [) J( `* A7 B; r
- & B# }/ ~6 E& P
- }
复制代码请注意我们是如何从助记词 "initAddress "中创建 initAddress 钱包(金库),并将其地址传递到 createFromConfig 我们的 MainContract. 至此,我们成功地初始化了我们的合约!祝贺您 :) 由于我们现在还要在消息正文中发送特定数据--让我们更新方法 sendInternalMessage 在我们的 wrappers/MainContract.ts. 我们将把它重命名为 sendIncrement,现在它还将接受一个值-increment_by。这将是当前合约存储值递增的一个数字。 我们还要记住,这是我们的合约为获取操作码而解析的同一个正文。因此,我们不要忘记首先存储一个 32 位整数(如合约代码所示,1 表示递增),然后再存储一个 32 位整数的递增值: - async sendIncrement(
: R n3 h. s# z - provider: ContractProvider,. v: K; n% t& t# n- w
- sender: Sender,: M, v9 G$ q. ?9 G
- value: bigint,
3 W4 P# C1 m; ?" X5 @ - increment_by: number i6 M( k5 X4 ?
- ) {- U+ V( f5 A0 o7 V
- F* J1 K j2 Q g! L
- const msg_body = beginCell()1 L: p* l% \9 l+ f+ p
- .storeUint(1, 32) // OP code9 C5 p$ W9 B8 |1 c# I$ W
- .storeUint(increment_by, 32) // increment_by value
/ c6 E- W' x9 V% \& ]( ]" } - .endCell();5 i4 u: n2 X$ E# {: X- g. `2 m
- 5 Z& v% ]( R$ A+ H( W$ d
- await provider.internal(sender, {: p9 E1 X, H! }
- value,$ z2 N; _5 i2 U3 |
- sendMode: SendMode.PAY_GAS_SEPARATELY,
' Q7 [8 r4 T" J. q7 D& H - body: msg_body,
. t' n" F+ b i6 O - });# y: N. L3 b; H; {" b
- }
复制代码 我们还需要更新封装器中的一个方法-getData。正如你所记得的,我们重新命名了 getter 方法,而且它现在返回两个值。让我们调整一下 wrapper 方法:7 S: O; v4 r( n2 E5 i: R3 }/ L
- async getData(provider: ContractProvider) {
# L2 M9 r6 \. L# j - const { stack } = await provider.get("get_contract_storage_data", []);
* {9 S, w9 {5 G- I5 c - return {8 C9 G& b3 {' K' Z
- number: stack.readNumber(),
; {5 G( `5 a# ^' \' p3 i7 m - recent_sender: stack.readAddress(),
8 |. ?5 L+ \: A* y! M9 X - };* C7 G2 s" n) ~& c0 |# A
- }
复制代码请注意,我们是如何读取 getter 方法的堆栈结果的。我们使用 readNumber() 读取当前值的整数。 现在,我们可以返回 tests/main.spec.ts,继续完成测试。以下是测试的更新代码: - // ... library imports + F7 |- ~ J# v9 N3 `. ?, T4 R9 a
- ) s3 Y6 A7 j; O3 Z: N& @
- describe("main.fc contract tests", () => {, e/ e5 V) ? p; y: q1 n
- it("should successfully increase counter in contract and get the proper most recent sender address", async () => {
; y1 w: J8 t# i' ]+ { |& Z1 E - const blockchain = await Blockchain.create();
5 A- k/ _) U/ |. n; j$ ? - const codeCell = Cell.fromBoc(Buffer.from(hex, "hex"))[0];
/ g* }" V, X: \; p0 @ - 0 G. z) S- }: u; T3 \* C& g
- const initAddress = await blockchain.treasury("initAddress");$ ]6 R/ C. q- o" X5 y- M. E& I
- ! E" C* r x8 [2 t
- const myContract = blockchain.openContract(
! _3 J9 @4 z( b, Z- }6 U6 u1 { - await MainContract.createFromConfig(
, y: ^0 e: v8 f% Y3 J p- p - {
+ i+ M A0 X1 q2 t" J - number: 0,) c. ?' l% V* N
- address: initAddress.address,
% b3 y: Q! g8 R - }, R% L7 X* F, m0 P! I }
- codeCell
# H4 {+ v9 J& }: V% K, E - )
$ c+ l$ d. Z8 B* \4 K - );- K, G5 Z* G& y! S/ W
- ; _, b! L( b1 M" k1 }5 D. u
- const senderWallet = await blockchain.treasury("sender");) }$ A8 C" G- Z: z; P
- + S ?; V s% l% {: G
- const sentMessageResult = await myContract.sendIncrement() a: G- y2 Q# n
- senderWallet.getSender(),$ j! O5 q5 [1 Q0 O0 }: @1 u3 m
- toNano("0.05"),
- m( H6 {$ @8 D* n, X) R7 t% F - 1& M; g5 }( z1 Q
- );, j, u- a( }: T1 i% i& B
- c) L1 }, T+ k* ?" X) g+ u
- expect(sentMessageResult.transactions).toHaveTransaction({. m, w# ^! y8 ~) h
- from: senderWallet.address,
- P% Z: W9 G6 k& W( X - to: myContract.address,0 ~1 c0 p8 Y* G* j- E* ^" t+ o0 Z
- success: true,
& w9 d2 _2 J0 L8 p - });
^4 {6 a1 Z% K: p* _
$ r1 E% [' H7 J$ {' w- const data = await myContract.getData();
$ _1 A0 z$ K/ q% a. C: S0 @0 t: x
H7 ^# J5 H; @- expect(data.recent_sender.toString()).toBe(senderWallet.address.toString());1 l# d+ U( x, N
- expect(data.number).toEqual(1);3 f7 ~8 M& J( x4 g
- }); H( q5 B4 E1 e5 w' T2 z
- });
复制代码我们在这里更新了什么? 现在我们可以使用 yarn test 命令。如果一切操作正确,你将在终端中看到以下内容: - PASS tests/main.spec.ts
3 d! l" t% T% M1 t" v9 c - main.fc contract tests8 P$ |( a, |9 o1 l
- ✓ should successfully increase counter in contract and get the proper most recent sender address (452 ms)
$ o+ F% m3 h! A+ O! s
) C3 @( A) L+ r4 _6 N% ?- q* S# y7 W- Test Suites: 1 passed, 1 total. p1 W% X" |, D, \; W
- Tests: 1 passed, 1 total
6 K; P, h- x; Z9 D; R0 b: A% a - Snapshots: 0 total- f+ K9 W* H( y4 i g5 i. \% E
- Time: 4.339 s, estimated 5 s
复制代码
7 B* _$ Z0 X" [
! {% d: J/ y; y, X2 s3 g+ r; u g+ N1 H |