正如我之前提到的,编写测试将是 FunC 合约编程中非常重要的一部分。让我们回顾一下上一课创建的测试: * E7 f& \+ Q8 O6 H8 q
- import { Cell, toNano } from "ton-core";3 r1 j$ J+ v6 p! K& G/ X
- import { hex } from "../build/main.compiled.json";8 y# t' o3 W& { R2 C9 x# _* i! j
- import { Blockchain } from "@ton-community/sandbox";: E% t" t6 h9 v) N: t
- import { MainContract } from "../wrappers/MainContract";
/ W5 S7 N3 H3 E2 o4 G9 P* L - import "@ton-community/test-utils";2 l1 ^6 Z6 K6 I- q+ a" [( O9 ~7 w3 E
- & v2 u8 L H$ f/ j
- describe("main.fc contract tests", () => {
, q3 y" b/ W6 H - it("should get the proper most recent sender address", async () => {9 S8 G$ H$ z1 f; K. T' n
- const blockchain = await Blockchain.create();
0 q) X0 T1 \* `7 L+ ?+ M+ e- r9 M; A - const codeCell = Cell.fromBoc(Buffer.from(hex, "hex"))[0];
4 n5 y1 Y. {& _, Z - 1 b: R" A; ~, U4 e$ F7 u" e
- const myContract = blockchain.openContract(% C- {. V- [" T6 l# [3 _/ G5 E
- await MainContract.createFromConfig({}, codeCell)& z6 E8 a- u! h- S
- );6 s7 v! P" \" }7 o& [
- 1 r, N) T$ x( e; o- @
- const senderWallet = await blockchain.treasury("sender");
9 V1 E& c2 D" U q# i9 M - ?3 w& q9 f: ` h- o5 I
- const sentMessageResult = await myContract.sendInternalMessage(
$ ]: n$ Q7 ]$ F) m - senderWallet.getSender(),! n2 F1 ]7 @. U$ l! f, j
- toNano("0.05")
$ q* ]+ J u! X& W" j( Z - );' l. e$ d9 V7 a X$ p n; Z1 Z
- . M' K* Z+ W/ r7 a {5 O
- expect(sentMessageResult.transactions).toHaveTransaction({$ E/ n3 f- y# y+ f" [# {9 M" }/ f
- from: senderWallet.address,
6 b8 `6 w1 L! P8 q# t - to: myContract.address,
9 Q7 v, [! v9 W& L( o$ n# X% Q3 K - success: true,$ ^( E: A' U& j. I( ?, g1 W
- });
/ Q. \# Y$ z8 K: H - . q J0 |+ z9 v+ k
- const data = await myContract.getData(); U: K" c3 [- @& j2 {0 C
9 X6 R0 u4 w5 b x8 H9 A- expect(data.recent_sender.toString()).toBe(senderWallet.address.toString());0 p \. K9 n4 H* |% R
- });" T$ C3 L1 F' D+ Y5 x' X
- });
复制代码值得一提的是,如果我们现在运行 yarn 测试,它将无法工作,因为我们的代码发生了很大变化。为了解决这个问题,我们需要做的如下: 让我们来处理初始数据。正如你所记得的,我们使用 createFromConfig 方法来初始化我们的合约,并通过以下方式将合约的初始状态数据设置为简单的空Cell: - static createFromConfig(config: any, code: Cell, workchain = 0) {* d0 o# A3 k/ h# T. c& q
- const data = beginCell().endCell();
: X1 e. @' r" l2 I0 G, t8 f - const init = { code, data };
- l- u& g2 Q0 D - const address = contractAddress(workchain, init);# P, m/ a: u5 Y+ @( f% P
- 9 C/ t" q% E! K0 d# A
- return new MainContract(address, init);
) Q- a! ~/ I8 g0 z8 t - }
复制代码然后在 main.spec.ts 中调用 createFromConfig:
1 n# e2 b0 I' B4 [. x" }- const myContract = blockchain.openContract(5 F; n- L* h9 D6 E. x1 j
- await MainContract.createFromConfig({}, codeCell)& V9 i+ X# e5 t9 \0 L7 Y+ z! I7 S
- );
复制代码到目前为止,我们传递的是一个空对象作为配置,但现在我们可以传递一些值,这些值实际上是我们初始化合约的适当数据所需要的。 让我们定义一下我们的 config 应该是什么样子。在 wrappers/MainContract.ts 中导入后,我们将定义一个名为 MainContractConfig 的新类型。 - // ... library imports
0 w6 ?9 f" w% I& k: B) X. a# C - - x' ]% g) Q( I
- export type MainContractConfig = {
5 w1 K N- j* l" I5 Z6 W - number: number;
3 {$ n. u: _: f* n, `. Y - address: Address;
% E" \: b9 M$ V0 w, Z. `, x - };
复制代码 我们将在同一封装文件中实现的另一个很酷的辅助函数是 mainContractConfigToCell.它将接收 MainContractConfig 类型的对象,将这些参数正确打包到Cell中并返回.我们需要这样做,因为你还记得,c4 存储空间是一个存储Cell:3 l" ]* ^2 `& J' ~4 o$ P+ K, s1 Q9 J
- // ... library imports
$ A8 k8 P+ B9 y5 I8 }. C1 K
" `' U6 b0 o' E, i- // ... MainContractConfig type* g2 M3 j3 f0 l5 n
- ! p& _- c6 E) Y8 ^
- export function mainContractConfigToCell(config: MainContractConfig): Cell {
7 X* H4 n9 y0 V" n; f: {: k; p - return beginCell().storeUint(config.number, 32).storeAddress(config.address).endCell();7 m4 B% [$ D- Z) H/ m; I
- }
复制代码 现在让我们更新同一文件中的 createFromConfig,不过是在 MainContract 类中。现在,它必须使用 mainContractConfigToCell 函数来形成初始状态数据 Cell:# S+ F' m* l! }9 v% H% k' `
- // ... library imports* ]/ ^: X. b% D- I+ I* u
- // ... MainContractConfig type
5 T5 Z! E$ t$ R' |/ a- J; F! q - // ... mainContractConfigToCell function
# P6 C @5 c: x$ e" `
$ o2 w3 i! u3 M$ @* \, k! d( S- export class MainContract implements Contract {% r6 g( B! n7 t8 P5 M4 _4 J. e. d
- constructor(! j2 Z* P7 K/ S5 ]/ X
- readonly address: Address,' `( {- g: x9 Q( G2 o1 f" _
- readonly init?: { code: Cell; data: Cell }1 O2 [+ A( r. ?4 C' P/ B
- ) {}
1 P) Y' {. U: ^& K% b! f - ' u1 { b, z$ r! v. ~/ N" y
- static createFromConfig(
{/ N; t. x. c/ P - config: MainContractConfig,
! v9 o- d$ } S4 A# B v; S( B5 R - code: Cell,) d% v$ ?4 b# J; J, X! s9 _4 u6 u
- workchain = 0) h, C3 d; I! U r+ x
- ) {
1 L9 Q, x0 W* f$ K - const data = mainContractConfigToCell(config);! }1 A# V! q5 c! b) W. a+ f
- const init = { code, data };
( t0 g; \. |9 G% S i# _ - const address = contractAddress(workchain, init);- D6 D' i2 I0 C! H$ u; K |9 L$ P% {
x4 d H' H6 k! c* P- return new MainContract(address, init);
# Z) B( j4 {2 L5 R5 F- M - }' z' M% R. _- d2 w% f8 l+ |5 F
- ( F2 u* c( w! u
- // ... rest of the methods& K y$ W8 y" F" J+ f
- }
复制代码现在,我们的 createFromConfig 方法已经准备就绪,让我们更新一下如何在 tests/main.spec.ts 文件中使用该方法:
! g3 l+ ^* Y3 v4 Q7 k( L i- // ... library imports1 O% j! W' j. v; H, J# Z
- 0 [+ f: Y9 T, C$ O) I' ]+ O1 ?1 y9 ^
- describe("main.fc contract tests", () => {$ Z9 D' G) w/ m6 J# `- _4 W4 v. W
- it("should get the proper most recent sender address", async () => {
% D `' Y+ t+ |6 ^$ [ - const blockchain = await Blockchain.create();
4 J' d: O2 k0 l, v. g# I% L - const codeCell = Cell.fromBoc(Buffer.from(hex, "hex"))[0];" M0 z+ M: \) R7 g5 e4 J+ c
5 g( t9 V, ~9 P& M& h( Q- const initAddress = await blockchain.treasury("initAddress");
# J0 o# Z I% r: |# j+ U - # O" @7 M* \$ u. Z% g3 |! d. L' I6 Y
- const myContract = blockchain.openContract(
& h+ E; b2 ^ c) Y( X( C3 B x3 h - await MainContract.createFromConfig(
' n7 s4 D& O/ X# L- b( o: Q - {
0 Q% J$ S: p$ \& o5 x4 P0 s2 V - number: 0,
$ G0 `+ t# @7 e. S; i4 Z - address: initAddress.address,
! a% K C+ `2 z1 ]" U - },
# h/ H; O+ E8 W: e! [% T4 f- b - codeCell) X) I0 i3 a& y4 S% a9 {3 T5 @
- )' I1 q; }1 [9 W e8 U8 j
- );* a- G g5 R8 l L* J5 l
-
& O% E6 W" E& V% b3 R. b' N - // ... the rest of old tests logic& n, \* p! u5 T) g2 k
-
( u1 u6 A6 V% _+ t. x r9 d - }
复制代码请注意我们是如何从助记词 "initAddress "中创建 initAddress 钱包(金库),并将其地址传递到 createFromConfig 我们的 MainContract. 至此,我们成功地初始化了我们的合约!祝贺您 :) 由于我们现在还要在消息正文中发送特定数据--让我们更新方法 sendInternalMessage 在我们的 wrappers/MainContract.ts. 我们将把它重命名为 sendIncrement,现在它还将接受一个值-increment_by。这将是当前合约存储值递增的一个数字。 我们还要记住,这是我们的合约为获取操作码而解析的同一个正文。因此,我们不要忘记首先存储一个 32 位整数(如合约代码所示,1 表示递增),然后再存储一个 32 位整数的递增值: - async sendIncrement(
/ J. z2 I( }7 L$ {" P H1 c. D - provider: ContractProvider,
. Y' O/ a, G6 r( v' \, ~2 T - sender: Sender,
) N% u7 Y$ I; H - value: bigint,/ j1 n% M0 r! x
- increment_by: number
( n/ _, K0 Y( A6 b# k - ) {. l3 j7 c d6 }: J3 @4 D; o
- ) Q0 j m' K" L) k0 A
- const msg_body = beginCell() w9 i" Y/ f0 L5 T% w& m, i
- .storeUint(1, 32) // OP code
/ A5 ^+ j9 G, o, z" S1 I# h. ~ - .storeUint(increment_by, 32) // increment_by value; R' m' z; |! M7 m8 h( A
- .endCell();
& C6 T0 Y/ ?( \' o - 7 \5 B# r* K) ?" I
- await provider.internal(sender, {
5 X7 r0 r; r2 j3 X. d- t7 ^ - value,
! o# d8 m0 O& ?( Y- | - sendMode: SendMode.PAY_GAS_SEPARATELY,
8 {" k0 r- N4 ], D+ ? - body: msg_body,4 a! k0 `" T/ y9 ?* ?
- });) p6 W+ L. X+ i9 K% T8 W8 x' |
- }
复制代码 我们还需要更新封装器中的一个方法-getData。正如你所记得的,我们重新命名了 getter 方法,而且它现在返回两个值。让我们调整一下 wrapper 方法:
% q) Q5 u# G/ K' w; s$ C- R+ H- async getData(provider: ContractProvider) {
; a) E h6 H) T3 E+ l- B) K' `; o - const { stack } = await provider.get("get_contract_storage_data", []);) B. K% w2 F/ t8 ~' e# m
- return {
5 p& t: {" J! m; t4 S4 h) V( Y - number: stack.readNumber(),
) E: J9 J9 T- t; g$ O z - recent_sender: stack.readAddress(),# ~' Y& H1 t# ?
- };5 O: C% x. J u+ Z
- }
复制代码请注意,我们是如何读取 getter 方法的堆栈结果的。我们使用 readNumber() 读取当前值的整数。 现在,我们可以返回 tests/main.spec.ts,继续完成测试。以下是测试的更新代码: - // ... library imports * U9 T. W# h/ T/ P' g5 o9 H
& l+ ]; e5 O5 I6 P, j) t: G- describe("main.fc contract tests", () => {
$ s- T& a( q: M- ^) `, j& G - it("should successfully increase counter in contract and get the proper most recent sender address", async () => {
& }' G" T2 O6 f2 x$ X# n& f2 N - const blockchain = await Blockchain.create();& ~4 x2 N1 F* {& z
- const codeCell = Cell.fromBoc(Buffer.from(hex, "hex"))[0];' A! a* f) m; w. w7 z8 D
_' j. ]/ |1 O( _9 V- const initAddress = await blockchain.treasury("initAddress");0 d+ X5 j# l# {
- 3 E. |: Q1 g8 @! `/ j7 o& x
- const myContract = blockchain.openContract(
1 m7 {' Z, u" Z: F4 Z; O# L - await MainContract.createFromConfig(1 F9 h) \9 u" [! `: ~# O+ }& J
- {. A9 T% t$ h+ Z7 h; t# H
- number: 0,
6 s5 S# S" r; O" i - address: initAddress.address,; W2 @* o9 O5 u: T6 T( f) D' q0 g
- },0 t/ U3 w( g" U5 h# v; D. U" j1 G
- codeCell3 @* X) u# f# o
- )
% C; T1 k' g+ Q6 a4 u- ?$ o - );, V" C% q1 n; b# K& f9 J& E7 ]- J+ o# B
3 R$ T, U+ L& f. r; n- const senderWallet = await blockchain.treasury("sender");) B; h1 L6 o6 p- T0 J* v
- 4 y3 L2 D! K) }! K9 [
- const sentMessageResult = await myContract.sendIncrement(
0 h1 s. f* u- W& Y; w - senderWallet.getSender(),
1 v' Z' b$ v. U2 I1 T2 e4 N - toNano("0.05"),! v2 f+ y& `& r
- 1
5 m X0 O+ {5 J3 i1 T0 t- I - );! E4 m0 l! Y" r) [: D; m
5 M$ i0 ?1 M! K5 S- expect(sentMessageResult.transactions).toHaveTransaction({0 h; C2 D' d7 R; e6 g$ @! `- I/ o! L
- from: senderWallet.address,2 q% N b+ V9 h$ J
- to: myContract.address,
! J8 ~/ N' \3 ?' [: N - success: true,! w2 }- P& b8 m# t
- });0 v2 r. v0 [. W, C' N" z& q
- / n# J5 R5 ]3 v: ~* d
- const data = await myContract.getData();0 s9 b% c/ ^: W
- 6 Z! `! ?, a$ f$ G
- expect(data.recent_sender.toString()).toBe(senderWallet.address.toString());
6 ]4 j; Z0 U% n7 [8 l& P; c' A - expect(data.number).toEqual(1);
, K% h, M" H. r9 t - });" {) I7 o$ s: D! r, F# n
- });
复制代码我们在这里更新了什么? 现在我们可以使用 yarn test 命令。如果一切操作正确,你将在终端中看到以下内容: - PASS tests/main.spec.ts2 z- j1 P1 M; z9 R1 X+ G0 C
- main.fc contract tests# W6 O: |$ u2 h7 s4 [1 {0 ~+ {3 \3 }5 a
- ✓ should successfully increase counter in contract and get the proper most recent sender address (452 ms) W5 y* A" b3 l" Z; s( f+ H' C$ V
- : b) A) Y9 g9 c. N; Z3 J& p' x
- Test Suites: 1 passed, 1 total
- T/ G4 Y2 F0 x& p - Tests: 1 passed, 1 total8 k& V( U' m. f, ^6 I
- Snapshots: 0 total
+ I% R% i2 P! c- A8 E- p) L - Time: 4.339 s, estimated 5 s
复制代码
2 V. v V3 p6 h0 {2 f) ]' O; o# j9 o/ ~9 u3 H0 W. R+ e! v% }
|