English 简体中文 繁體中文 한국 사람 日本語 Deutsch русский بالعربية TÜRKÇE português คนไทย french

简体中文 繁體中文 English 日本語 Deutsch 한국 사람 بالعربية TÜRKÇE คนไทย Français русский

回答

收藏

3.4 测试工作流程和编写测试

开源社区 开源社区 5932 人阅读 | 0 人回复 | 2025-03-07

本帖最后由 riyad 于 2025-3-8 23:13 编辑 ! o! i7 F' X# y0 ~. @
, q( m+ E# ?6 H: b, R3 J$ F
我们已经编写了第一个 FunC 合约,并已成功编译。接下来要做什么呢?
在这一课中,我们将学习如何确保我们的合约代码确实能按预期运行。让我提醒你我们对代码的期望:
我们的合约应该在每次收到消息时保存一个发件人地址,并且在调用 getter 方法时返回最新的发件人地址。
很好,但我们如何确保它能正常工作呢?我们可以将它部署到区块链上(我们很快就会在下一课中这样做), TON 有一些工具,可以让我们在本地模拟某些行为.
这需要借助 sandbox. 这 library 允许您模拟任意的 TON 智能合约,向它们发送消息并运行获取方法,就像它们部署在真实网络上一样。 因为我们有自己的 TypeScript 环境,所以我们可以借助另一个库创建一系列测试 - jest. 这样,我们就有了一个测试套件,可以用不同的输入数据模拟所有重要行为,并检查结果。这是编写、调试和全面测试合约的最佳方法,然后再将它们发布到网络上。

8 O  T  T  w, t8 a准备测试套件
首先,假设你现在位于项目的根目录,让我们安装 sandbox, jest 以及一个我们需要与 TON 实体交互的库 - ton:
% V8 b0 q& b: Z+ y0 `! D- p
  1. yarn add @ton/sandbox jest ts-jest @types/jest @ton/ton --dev4 Q' q$ G5 i- \
复制代码
我们还需要在项目根目录下为 jest 创建一个 jest.config.js 文件,内容如下:
  1. module.exports = {# d! j) I, a$ L9 p
  2.     preset: 'ts-jest',
    0 R  n8 ^, X. y! {$ [! }
  3.     testEnvironment: 'node'," @& \6 N5 S2 K" x7 k3 t8 I
  4. };
复制代码
现在,我们创建一个新文件夹 test,其中包含文件 main.spec.ts
  1. mkdir tests && cd tests && touch main.spec.ts) m3 I" A+ s& x- X
复制代码
我们设置好 main.spec.ts 文件,以便编写第一个测试:
  1. describe("main.fc contract tests", () => {+ X( R( b2 `. W7 s1 V) z% z' p; e

  2. % d" B8 e' \+ Q
  3.   it("our first test", async () => {2 M# x& Z4 {  x1 R; y8 b4 t
  4. # o# y: x  u( }% _) [' }
  5.   });
    4 N" W) ~  N# Z+ d  H! v
  6. ! {" q! G. W" z0 p! |! E0 Q! t
  7. });
复制代码
如果你从未编写过 TypeScript 测试,你一定要试试这种方法。在编写 TON 智能合约时,你会将一半以上的编程时间花在编写测试上。还记得我们的太空卫星例子吗?在这里也是一样,我们甚至在将合约部署到 testnet 之前,就已经模拟了每一种重要情况。
现在尝试运行命令 yarn jest 的根目录中。如果你已经正确安装了所有程序(跟我一起一步步安装),你应该会看到下面的内容:
  1. PASS  tests/main.spec.ts0 I4 c0 D: A! O1 k% I) K
  2.   main.fc contract tests
    + ~( V' n* f. ^2 C1 f* V
  3.     ✓ our first test (1 ms)
复制代码
很好,让我们立即在 package.json 文件中创建另一个脚本运行快捷方式:
3 g, m, v5 ?/ j( o1 {
  1. {2 x! g, @0 x6 p' E% ~& v6 f. d
  2.   ... our previous package.json keys
    2 I2 j  v& W4 D6 ?4 u; G
  3.   "scripts": {
    . N- G' @+ z. k# P( c9 w5 x" I
  4.     ... previous scripts keys
    $ `, `0 O6 ]$ J7 `+ m: b
  5.     "test": "yarn jest": @8 m7 \! Y) c/ O4 s
  6.   }
    ' `" z/ C+ F4 b8 H" q( r1 A& y
  7. }
复制代码
创建合约实例
为了编写第一个测试,我们需要了解如何借助 sandbox.
我们之前讨论过,我们的合约代码在编译后会存储为一个 Cell。在 build/main.compiled.json 文件中,我们有一个 Cell 的十六进制表示法。让我们把它导入我们的测试文件,并从以下文件中导入 Cell 类型 ton-core:
  1. import { Cell } from "@ton/core";  f5 x1 d1 `8 b. m
  2. import { hex } from "../build/main.compiled.json";
    . V! c: b- c/ m$ a/ q$ A1 p* {
  3. 0 _/ f) a' S3 e* p
  4. describe("main.fc contract tests", () => {
      w4 \0 e" c7 r# c9 V

  5. # T1 L5 Q3 u" H4 y+ p
  6.   it("our first test", async () => {
    4 I. E( j4 s0 i1 y3 ^9 {, B& ]
  7. + c* X  a. w6 l
  8.   });
    - V: L( ?! x5 W

  9. % C* b# n; O/ ~; N
  10. });
复制代码
现在,要恢复十六进制并获得真正的 Cell,我们将使用这条命令: const codeCell = Cell.fromBoc(Buffer.from(hex, "hex"))[0].
我们用十六进制字符串创建一个缓冲区,并将其传递给 .fromBoc 方法。
现在让我们来看看 sandbox quick start guide. 我们看到,在获得合约实例之前,我们必须先获得区块链实例。
让我们从 sandbox 库,并调用它的 .create() 方法。
  1. import { Cell } from "@ton/core";
    / c- a# j  @& m8 m$ q" b1 L
  2. import { hex } from "../build/main.compiled.json";+ C  D8 S) b; [3 A# i
  3. import { Blockchain } from "@ton/sandbox";+ P: a* D, B  {

  4. % y5 S( k, c) v; e* t; Z# O- m: n
  5. describe("main.fc contract tests", () => {
    * S  d" F9 `5 W, V2 {1 d
  6.   it("our first test", async () => {- `% R9 H9 a% \8 K
  7.     const blockchain = await Blockchain.create();3 d/ ~8 X" a! V9 @& S/ U, n
  8. 6 D2 ]( D* [* I3 `. ]
  9.   });5 m. u* v1 |* @* j. S
  10. });
复制代码
现在,让我们准备好获取一个合约实例来进行交互。Sandbox 的文档指出,推荐的使用方法是使用 Contract 从 ton-core
这对我们意味着什么?让我们创建一个新文件夹 wrappers 和一个名为 MainContract.ts 其内. 该文件将实现并导出我们合约的包装器。
  1. mkdir wrappers && cd wrappers && touch MainContract.ts
    $ f/ t/ S; \: S- |5 c0 ~- [
复制代码
确保从项目根目录运行此命令序列
打开 MainContract.ts 编辑用. 让我们导入一个 Contract 从 ton-core 库,然后定义并导出一个实现 Contract.
  1. import { Contract } from '@ton/core';
    ' \1 f% k, ~7 f1 O' N
  2. 3 _* F9 B, u! [, ~0 O
  3. export class MainContract implements Contract {
    8 a+ R  `7 w& K& G$ t8 x$ b
  4.    
    ! R& f8 ?' Y2 U5 z; G: T
  5. }
复制代码
如果您查看一下 Contract 接口 - 你会看到它需要 address, init 和 abi 参数。 我们只会将 address 和 init 用于我们的目的。 为了使用它们,我们要为我们的 MainContract 类定义一个构造函数。
如果您不知道我们这里所说的类和构造函数是什么意思,那么您最好多读一些关于面向对象编程(Object Oriented Programming)的内容。FunC 并不要求这样做,但为了更好地使用 TypeScript 进行测试,您应该了解这些基本概念。
  1. import { Address, Cell, Contract } from "@ton/core";2 N, @4 Y4 E& y5 ]
  2. % x; q5 J$ C/ {8 B" R
  3. export class MainContract implements Contract {. G0 D# D- i+ }/ I& P8 Q0 }  r
  4.   constructor(4 H7 |: W' Z6 k5 C( F
  5.     readonly address: Address,: c) W3 ?) y6 L3 U. v3 h8 i
  6.     readonly init?: { code: Cell; data: Cell }6 `" Q% {! J  I6 P! P1 {
  7.   ) {}! }4 x0 W9 u/ R" C
  8. }
复制代码
init 属性非常有趣。它具有我们合约的初始状态。代码显然就是合约的代码。数据则更有趣一些。还记得我们在讨论合约的 c4 持续存储吗? 有了这个 data 在Cell中,我们可以定义一旦我们的合约首次执行,该存储空间中会有哪些内容。这两个值都有 Cell 类型,因为它们在合约生命周期内存储在 TVM 的内存中。相同类型 code 和 data Cell还用于计算我们合同的未来地址。
请注意,我们还导入了 Address 和 Cell 从 @ton/core 库.
在 TON 中,合约的地址是确定的。我们甚至可以在部署合约之前就知道它们。我们将通过几个步骤了解如何做到这一点。
现在,让我们为我们的 MainContract 类 - createFromConfig. 我们暂时不会使用任何配置参数,但今后我们会假设需要输入数据才能创建合约实例。
  1. import { Address, beginCell, Cell, Contract, contractAddress } from "@ton/core";5 e3 I, |6 y* t6 l& u$ X
  2. & G* R3 O" S7 U. j
  3. export class MainContract implements Contract {# g2 ~  \! x) f
  4.   constructor($ b4 K7 N5 c5 x. X8 ]2 @& |2 G) H# y
  5.     readonly address: Address,
    & y. S" N& h, N1 r! F5 L
  6.     readonly init?: { code: Cell; data: Cell }% [3 ?# U# H  x4 i" P* {
  7.   ) {}
    " [% i% R: \; a  L
  8. & z/ M& I7 _9 ?2 z4 Q/ l! c0 o" H
  9.   static createFromConfig(config: any, code: Cell, workchain = 0) {9 p: {# x3 r. O% Y4 D
  10.     const data = beginCell().endCell();
    # B" h+ ^9 a0 ]. K. y9 }
  11.     const init = { code, data };& a% X/ N" T. Y% l1 O8 k
  12.     const address = contractAddress(workchain, init);
    / T6 a. f3 w8 s1 i

  13. : F* X+ F1 X0 A) F# w# ?, k
  14.     return new MainContract(address, init);( }, Z+ x/ M& ~# G, A) Z
  15.   }
    , y) O5 E7 g+ U3 @
  16. }
复制代码
我们的 createFromConfig 方法接受一个 config 参数(暂时忽略), code 是一个包含我们合约的编译代码的 Cell 和一个 workchain 定义了合约要放置的 TON 工作链。目前,ton 上只有一个工作链 - 0。
为了更好地理解这段代码,让我们从返回的结果开始。我们正在创建 MainContract 类的一个新实例,并按照其构造函数中的定义,我们必须传递其合约的未来 address (如前所述,我们可以计算出)和合约的 init 状态。
我们通过从 @ton/core 库导入的函数计算地址. 通过 workchain 和 init 状态参数,以便获取它。
init 状态只是一个具有 code 和 data 属性的对象.  code 被传入至我们的方法中,而 data 现在只是一个空Cell。我们将学习如何将 config 数据转换成 Cell 格式,以便我们可以使用 config 数据中 init 我们的合约状况。
总而言之 createFromConfig 正在接受一项 config 数据,我们将来会将这些数据存储在合约的持续存储和 code 的实例。作为回报,我们会得到一个合约实例,我们可以借助 sandbox.
让我们回到我们的 tests/main.spec.ts 并执行以下步骤:
  • 导入我们的包装器
  • 以十六进制形式导入合约的编译代码
  • 转换为Cell
  • 使用沙盒的 openContract 与我们的新包装器一起,得到一个合约实例,最终我们可以与之交互

    ) ^# _5 O' G2 Y/ ~

1 ]& `" Z; n/ g* |; u/ V
  1. import { Cell } from "@ton/core";* h& r  T, V5 x, m) s1 J
  2. import { hex } from "../build/main.compiled.json";' w( e! j& y7 H" C1 d
  3. import { Blockchain } from "@ton/sandbox";
    * r* {# H- t2 T( q
  4. import { MainContract } from "../wrappers/MainContract";
    " d' V8 t" D! k! A, _

  5. + M. Y: y% R; P* b4 J% Y% W
  6. describe("main.fc contract tests", () => {
    % O: l3 S0 Q( |! r0 k; R9 l
  7.   it("our first test", async () => {
    5 e8 p/ a6 ?& R% i. [  ~
  8.     const blockchain = await Blockchain.create();& `0 _; |5 V" T$ O
  9.     const codeCell = Cell.fromBoc(Buffer.from(hex, "hex"))[0];2 C3 b7 g: r* S$ C1 H! u, [% z
  10. * w- p  K5 m, U
  11.     const myContract = blockchain.openContract(2 q, V0 w' f. K5 ~# Q# d" G  a3 g2 |
  12.       await MainContract.createFromConfig({}, codeCell)9 P9 C) B* G' \3 _8 g. Z& A% C2 u9 U
  13.     );6 }* n, K# D* \8 S& D& m8 F
  14.   });  K0 ?% ]( {0 A, ~' K/ H
  15. });
复制代码
至此,我们就有了一个智能合约实例,我们可以通过许多与真实合约类似的方式与之交互,以测试预期行为。
与合约互动
@ton/core 库为我们提供了另一个伟大的组件,名为 Address. 众所周知,TON 区块链上的每个实体都有一个地址。在实际中,如果您想从一个合约(例如钱包合约)向另一个合约发送信息,您需要知道两个合约的地址。
在编写测试时,我们会模拟一个合约,当我们与合约交互时,我们模拟的合约地址是已知的。不过,我们还需要一个钱包,用来部署我们的合约,另一个钱包用来与我们的合约交互。
在 sandbox 这样做的方式是,我们称之为 treasure 方法,并为其提供一个助记词: const senderWallet = await blockchain.treasury("sender");
模仿内部信息
让我们继续编写测试。既然我们已经有了一个合约实例,那就向它发送一条内部信息吧,这样我们的发件人地址就会保存在 c4 存储空间中。我们还记得,要与我们的合约实例交互,我们需要使用包装器。
回到我们的文件 wrappers/MainContract.ts 并在封装器中创建一个名为 sendInternalMessage.
最初看起来是这样的:
  1. async sendInternalMessage(7 U( _; V4 j1 O! I* {7 f
  2.     provider: ContractProvider,
    : u4 F; ^9 w9 u! L( g' ?, Q
  3.     sender: Sender,  J3 r9 X# l9 h2 k
  4.     value: bigint
    # f$ _7 ?+ j7 [/ q# z7 @5 e4 G% |
  5.   ){' S5 ]* _* Q5 J' Z' W* T7 e! w4 c
  6.     6 l9 N4 k9 _( N5 h2 \' n' u; V! L
  7. }
复制代码
我们的新方法将接收 ContractProvider 类型的参数、Sender 类型的信息发送者和信息值 value.
通常情况下,使用此方法时我们不必担心传递 ContractProvider,它将作为合约实例内置功能的一部分在底层传递。不过,不要忘了从 ton-core 库中导入这些类型.
让我们在新方法中实现发送内部信息的逻辑。它看起来是这样的:
  1. async sendInternalMessage(' Z5 u+ d- R' s. t! z
  2.     provider: ContractProvider,
      E: P" ], o7 ?0 x* O
  3.     sender: Sender,
    8 z) K3 r1 K' ]$ \
  4.     value: bigint: }7 ^( q# ?0 Z) @) z% I8 ^* q$ N& \+ q
  5.   ) {
    5 t! s. v1 }! M0 }. A
  6.     await provider.internal(sender, {
    % {; w0 S. f* w1 D
  7.       value,
    ' }6 g$ ?* U; u. \
  8.       sendMode: SendMode.PAY_GAS_SEPARATELY,
    ! X6 C8 O0 o3 P' D
  9.       body: beginCell().endCell(),
    4 v$ j+ I3 @5 N( L8 g) V
  10.     });3 D- v* y* O, Y  V# d  K1 I9 R
  11.   }
复制代码
您可以看到,我们正在使用 provider 并调用其名为 internal. 我们通过了 sender 作为第一个参数,然后我们用参数组成一个对象,其中包括:
  • value 的信息量(nano格式的TON币数)
  • sendMode - 我们使用由 @ton/core, 有关这些模式如何在底层工作的更多信息,请参阅 documentation page 致力于此
  • body 这应该是一个包含信息正文的Cell,但我们暂时将其保留为一个空Cell

    / [" b3 d5 W$ ^2 j1 J9 f5 V2 a
请不要忘记将我们使用的所有新类型和实体从 @ton/core 库.
因此,wrappers/MainContract.ts 的最终代码如下所示:

" y# I6 W( V0 A; R. Z4 ~3 J* j: [
  1. import { Address, beginCell, Cell, Contract, contractAddress, ContractProvider, Sender, SendMode } from "@ton/core";
    2 I3 n9 I! Q( l# r4 }6 K* T  ]: {

  2. $ ^5 c' j0 Q$ M1 `$ ^+ u2 O8 ~
  3. export class MainContract implements Contract {$ A: E2 z; s: f/ [  N1 n. F
  4.   constructor(% g7 Y7 x/ t3 d. ^
  5.     readonly address: Address,' z" p3 J/ g4 d( b' v9 z
  6.     readonly init?: { code: Cell; data: Cell }
    $ M) O- r7 C5 Z4 B% F& C
  7.   ) {}
    " m6 F5 a( p2 F

  8. - j1 W" ^* I! g4 J. k
  9.   static createFromConfig(config: any, code: Cell, workchain = 0) {
    % U; j+ e' @; x7 P- B/ ?9 o
  10.     const data = beginCell().endCell();
    4 f" c. y) m; A) y( z: D8 Z( k
  11.     const init = { code, data };3 A9 _$ O, g! l# v& E8 W) o  {
  12.     const address = contractAddress(workchain, init);
    6 J/ X/ ^5 Z! ^% I
  13. & Z. n- \* _8 \- b, o% L: l
  14.     return new MainContract(address, init);! G* f- i# b% x
  15.   }
    % K) i4 B. v. Q* K

  16. 3 J" N0 I: Z6 S7 ^6 w! @% b  k
  17.   async sendInternalMessage(6 [1 I+ Z! g; U! ?6 z
  18.     provider: ContractProvider,
    / Q4 C9 i! ~# S' v1 p* m: \
  19.     sender: Sender,
    4 D2 h" N4 t& r, ~4 H; T' H
  20.     value: bigint( q# P# x+ a2 V3 w1 S
  21.   ) {2 v3 F: c( _$ \
  22.     await provider.internal(sender, {" d! \$ c5 ]) D5 s0 y  Y2 s( x) s
  23.       value,
    / L, R$ F; u5 b7 `% X7 k
  24.       sendMode: SendMode.PAY_GAS_SEPARATELY,
    0 L0 |  Z" ^: Y& M6 B. n7 O2 W
  25.       body: beginCell().endCell(),' b  c% m/ v! p- N7 C, Z
  26.     });
    # a/ I5 g* j3 X% P1 i$ R9 J) H
  27.   }; ], e& _0 y) h5 @
  28. }
复制代码
在我们的 tests/main.spec.ts 中调用该方法会是这样:
  1. const senderWallet = await blockchain.treasury("sender");2 s( H" O8 i# [9 t/ J8 G
  2. myContract.sendInternalMessage(senderWallet.getSender(), toNano("0.05"));
复制代码
请注意我们是如何使用 toNano 辅助函数 @ton/core 库将字符串值转换为nano gram格式。

& `" G+ r) C/ h, R
5 O& f* c4 i7 a: J7 p调用 getter 方法
我们需要在合约包装器上再创建一个方法,即 getData 方法,该方法将运行合约的 getter 方法,并返回 c4 存储器中的结果。
这就是我们的 getter 方法:
  1. async getData(provider: ContractProvider) {+ p( A* Z) g7 v4 j8 J5 a# V' S
  2.     const { stack } = await provider.get("get_the_latest_sender", []);2 {- W$ C( Y1 y
  3.     return {( M- c: W7 O) C: _" h) u4 I' e8 \8 ^2 h
  4.       recent_sender: stack.readAddress(),5 f2 P3 _7 F) E# `0 v4 T# R- r
  5.     };3 G( V0 x; {* F1 S" f
  6. }
复制代码
就像我们发送内部信息一样,我们使用的是提供发送者及其方法。在本例中,我们使用的是 get 方法。 然后,我们从接收到的 stack 并将其作为结果返回。
编写测试
就是这样。我们已经完成了所有的准备工作,现在要编写实际的测试逻辑。下面是我们的测试场景:
  • 我们发送内部信息
  • 确保发送成功
  • 我们调用合约的获取方法,并确保调用成功。
  • 我们将从 getter 接收到的结果与 from 我们在原始内部信息中设置的地址。
    $ c' b* k, ]% \) [* h& M
看起来非常简单可行!让我们行动起来吧
沙盒团队又为我们提供了一个神奇的测试工具。我们可以安装额外的 @ton/test-utils 软件包,方法是运行 yarn add @ton/test-utils -D
这将使我们能够使用 .toHaveTransaction 为 jest matcher 添加额外的辅助工具,以方便测试。我们还需要在我们的 tests/main.spec.ts 安装后。
让我们看看基于上述场景的测试代码是怎样的。
  1. import { Cell, toNano } from "@ton/core";
    0 ]9 R4 M+ y, z* v7 v- F" q
  2. import { hex } from "../build/main.compiled.json";+ |5 C# I3 Z; T8 t9 Z
  3. import { Blockchain } from "@ton/sandbox";& g4 p3 ^5 o" Z' C: k
  4. import { MainContract } from "../wrappers/MainContract";
      ~" I7 g# h) T3 m4 P% X: Y
  5. import "@ton/test-utils";! L& h7 S, N* A, c

  6. ) G3 \) m1 K; i  K( Z9 y
  7. describe("main.fc contract tests", () => {
    * f5 y  J5 x7 Q4 K' b
  8.   it("should get the proper most recent sender address", async () => {
    ) E& H1 T& k4 O3 ]3 u
  9.     const blockchain = await Blockchain.create();
    7 `. \6 y0 M- `- [6 w5 K: J0 H* L
  10.     const codeCell = Cell.fromBoc(Buffer.from(hex, "hex"))[0];
    / ~# w' p! `* u' _8 s
  11. . E; \' G; v" X5 c4 f$ k
  12.     const myContract = blockchain.openContract(
    9 {9 F2 U$ s+ r6 P
  13.       await MainContract.createFromConfig({}, codeCell): Y/ Y7 R  R+ r! l5 r
  14.     );2 K' T" S- M/ D; c% i' \

  15. 5 c1 H6 G4 n  Q! \0 ^; Q
  16.     const senderWallet = await blockchain.treasury("sender");0 X+ s! l, W& M

  17. 0 z! X, `/ \9 |- g: K/ [/ f
  18.     const sentMessageResult = await myContract.sendInternalMessage(, ^; B3 a3 T( m9 i
  19.       senderWallet.getSender(),- K( Q4 i6 Y( f$ M4 n5 S
  20.       toNano("0.05")
    3 |1 R% m# b7 }+ G) y1 R7 n
  21.     );$ k- A2 d/ P3 J; Z% Y# m6 y8 j

  22. 0 r/ K. ], l0 G. _1 w* ?% k% ~$ ?
  23.     expect(sentMessageResult.transactions).toHaveTransaction({  _" J+ f0 J7 R  Y5 t* f
  24.       from: senderWallet.address,9 C" }- V  k8 m6 W, N& f; d
  25.       to: myContract.address,& P3 @3 M& \$ j6 K1 T
  26.       success: true,
    , g6 l5 |: @& t# h, U; F6 i
  27.     });0 D1 l  g  t$ {1 z$ w6 Y) R

  28. / @6 V, T. I: {( s+ c3 E, O
  29.     const data = await myContract.getData();
    , ?4 a. z! S6 B: y  Y+ W
  30. 4 @' O7 Q+ @4 e" e: I, l& [7 {
  31.     expect(data.recent_sender.toString()).toBe(senderWallet.address.toString());: r/ u. j2 d# `) `/ Y
  32.   });; R5 X/ |" T1 A, ~+ a3 Y
  33. });
复制代码
我们使用 Jest 功能来确保这一点:
  • 内部信息发送成功
  • 获取方法成功
  • 获取方法返回结果
  • 从获取方法返回的地址等于我们在信息中使用的地址  from 地址
    ' p& y  A) F/ D0 J. |
我们还将 "我们的第一次测试 "更名为 "应获得正确的最新发件人地址",因为我们希望测试名称始终可读。
运行测试
瞧!我们已经准备好运行测试了。只需运行我们的命令 yarn test 在终端中,如果你和我一起做了所有的事情,你也会得到类似的结果:
  1. PASS  tests/main.spec.ts
    ) M8 ?6 O9 P' C
  2.   main.fc contract tests
    / `( n% `4 P, @. k. V. w% [9 k
  3.     ✓ should get the proper most recent sender address (444 ms)
    . h7 \* e: i) L

  4. ; H& a9 P8 {4 x
  5. Test Suites: 1 passed, 1 total  z- r, V7 e/ K% J# [
  6. Tests:       1 passed, 1 total
    ( e4 }6 T9 k7 L4 n& f6 o2 u% U  I
  7. Snapshots:   0 total% a' ]4 a2 W' B$ \& c) c
  8. Time:        4.13 s, estimated 5 s
复制代码
我们要做的最后一件事是确保每次运行测试时,同时运行编译器脚本。这有助于提高我们的工作效率。大部分开发工作都是编写功能代码,然后运行测试。让我们简化这一切:
更新 package.json 文件,使其看起来像这样:
  1. {* a# T/ C" y+ U" A  a1 B
  2.   ... our previous package.json keys( `; S. q( e1 `: s1 d
  3.   "scripts": {* v" `% B! M% ^) o! u2 {5 m
  4.     ... previous scripts keys9 H1 w3 ^6 U" _- i- \
  5.     "test": "yarn compile && yarn jest"- J/ r$ L8 g: N0 M$ ]. N
  6.   }1 G/ X/ f0 u& @1 ^8 X& ]
  7. }
复制代码
在下一课中,我们将构建部署途径,并学习如何在链上测试真正的部署合同
9 i4 R8 W# F8 R: t; d, U' `7 d" J1 U  o8 x+ y
! A2 h$ l! u/ X" B& [4 z! ^
分享到:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则