使用 Cadence 测试框架简化智能合约测试

2023-06-24   出处: medium  作/译者:Satyam Agrawal/Yilia


  Cadence 是一种旨在 Flow 区块链上制作安全智能合约的语言,本文是为那些已经对使用 Cadence 编程语言编写智能合约有基本了解的人编写的。

为什么选择 Cadence 测试框架?

  Cadence 测试框架提供了一种为用 Cadence 编写的智能合约编写测试用例的新颖方法。在 Cadence 测试框架出现之前,开发人员必须精通 JavaScript 或 Go-Lang 才能编写测试用例。然而,这个新框架为可能不具备这两种语言中任何一种语言专业知识的开发人员提供了解决方案。在快速发展的技术世界中,开发人员精通多种语言是很常见的,但并不是每个开发人员都具备这些技能。因此,拥有一个使开发人员能够直接用 Cadence 语言编写测试用例的框架非常重要。Cadence 测试框架是释放 Cadence 智能合约编程语言全部潜力的关键。通过简化测试流程,它降低了开发人员的进入门槛,使他们能够专注于掌握 Cadence。这反过来又加速了 Cadence 的采用,并使开发人员更容易创建安全可靠的智能合约。

如何使用Cadence测试框架编写测试?

我们先从准备工作开始吧。

设置

  开发人员应使用 flow-cli v0.41.0 或更高版本。安装或升级 flow-cli 后,请确保 cli 提供 flow test 命令。可以通过运行以下命令来验证:
flow test --help
输出看起来像这样

snapshot_1
而放置 cadence 测试用例的推荐目录结构如下。

snaptshot_2
  建议如果待测试的合约名称为Abc.cdc,则对应的测试文件应命名为Abc.cdc。但需要注意的是,Cadence测试框架对测试文件的命名约定没有任何限制,开发人员可以自由选择自己喜欢的名称。上述 Offers 是被测试的合约名称。mocks 目录用作交易、脚本和合约的存储库,这些交易、脚本和合约不能存储在根交易、脚本和合约目录中。为了促进成功测试,开发人员可能需要与测试合约不直接相关的交易,例如 setup_account_to_receive_fungible_token.cdc。建议将这些类型的事务保存在mocks目录中。

编写测试用例

  在编写测试之前,需要完成基本的设置过程,包括建立帐户和部署合约,以执行测试用例。

snapshot_3
我们把上面的代码逐行解码一下,一起理解一下。
第一行:
import Test
  测试智能合约是在 Cadence 中编写测试用例的不可或缺的组件。它提供了在 Cadence 中创建测试用例和编写测试所需的构建块。如果没有测试契约,就不可能编写测试用例。
第三行:
pub var blockchain = Test.newEmulatorBlockchain()
  上面提到的语句用于创建一个新的区块链环境实例,模拟流模拟器网络并促进交易和脚本的执行。从技术上讲,它是一种结构数据类型,提供与区块链相关的功能,例如 createAccount 和 addTransaction。
第六行-第八行:

// Setup accounts.
pub let signer = blockchian.createAccount()
pub let offers = blockchain.createAccount()
pub let coreContractsAccount = blockchain.createAccount()

  创建账户是执行交易执行、智能合约部署和脚本执行等各种操作的基本要求。在所提供的代码中,建立了两种类型的帐户:一种用于签名者,另一种用于部署它的智能合约。为 Offers 合约指定的账户称为 Offers,而为部署核心合约(包括 FungibleToken、NonFungibleToken、MetadataViews 等)指定的账户称为 coreContractsAccount。这种安排有利于测试过程。createAccount 是区块链实例中的内置函数,它提供 Account 结构作为返回值。

/// Account represents info about the account created on the blockchain.
///
pub struct Account {
    pub let address: Address
    pub let publicKey: PublicKey
}

第十行:
pub fun setup() { }
  执行测试文件时会自动调用设置函数。它作为测试用例编排的初始点,执行配置测试环境和部署智能合约等任务。
第十二行至十五行:

// Let the CLI know how the above addresses are mapped to the contracts.
blockchain.useConfiguration(Test.Configuration({
  "CoreContractsAccount": coreContractsAccount.address,
  "Offers": offers.address
})

  在 Cadence 项目中,常见做法是将文件位置声明为导入,并在 Flow CLI 配置文件中指定特定于网络的地址。在为此类项目设计测试时,可能还需要指定测试过程中使用的地址。然而,由于账户是动态创建的,地址是在测试过程中动态生成的,因此在配置文件中静态指定地址是不可行的。为了解决这个问题,测试框架使用区块链中的 useConfiguration(_configuration: Test.Configuration 函数,启用用于测试目的的地址规范。
第十七行至十九行:

deploySmartContract("FungibleToken", coreContractsAccount, "../../../contracts/core/FungibleToken.cdc")
deploySmartContract("NonFungibleToken", coreContractsAccount, "../../../contracts/core/NonFungibleToken.cdc")
deploySmartContract("Offers", offers, "../../../contracts/Offers.cdc")

  如前所述,该声明用于在指定地址设置合约,如 coreContractsAccount 中 FungibleToken 的部署所示。 FungibleToken 的代码是从其文件路径获取的,即 ../../../contracts/core/FungibleToken.cdc

snapshot_4
  创建实用函数deploySmartContract是为了方便合约的部署。该函数利用内置函数 Test.readFile 从给定文件路径检索合约,并利用 Blockchain.deployContract 将合约部署到网络。
让我们来写一些测试用例:

snapshot_5
  在Cadence测试框架中,每个测试用例都表示为一个函数,并且必须以单词test为前缀才能被测试框架识别为有效的测试用例。建议为这些测试函数提供描述性名称,以便读者只需阅读其名称即可了解测试的预期结果。
  使用前缀execute来指代在测试网络上生成和提交交易的函数,而前缀get指代执行脚本并从链中检索数据的函数。
  创建用于在测试用例中执行操作的帐户第 3 行到第 7 行。然后,从第 10 行到第 13 行,调用函数executeSetupVaultAndMintTokenTx 为指定帐户设置 FungibleToken Vault 并铸造相应的代币。第 15 行的executeCreateOfferTx 函数用于执行propose_offer.cdc 交易。它是一个辅助函数,用于提交交易并验证交易是否成功执行。

snapshot_6
  在 snapshot_6 的第 14 行中,检索 suggest_offer.cdc 文件的内容,并将其与作为签名者的交易签名者以及第 18 至 27 行中包含的一组参数一起传递给 txExecutor 函数。这些参数是执行事务所必需的,而预期错误和预期错误类型作为 nil 传递,如 snapshot_5 中所示。这两个参数将在后续部分中进一步详细讨论。让我们仔细看看 txExecutor 函数。

snapshot_7
  在 snapshot_7 中,第 3 行到第 8 行创建了具有必要参数的交易。blockchain.executeTransaction 函数促进了第 10 行创建的交易实例的执行。Js 测试库通过 Js 测试库提供了对交易失败的自动处理。 Cadence 测试框架中的 ShouldRevert 函数需要显式处理 txResult.error。这使得能够通过修剪错误消息以与预期错误参数保持一致来设计失败的测试用例,从而提供一种用于编写如下所示的失败测试的机制。

snapshot_8
  在 snapshot_8 的第 14 行,传递了预期的错误消息,以便与 txExecutor 函数中事务返回的错误进行比较。测试用例的成功取决于预期错误的匹配。第 15 行指定错误类型,因为事务返回的每个错误对于实际错误语句都有唯一的起点。开发人员可以通过计算错误跟踪中的字符数来确定这一点。snapshot_9 中的函数包含编写的测试用例的错误语句的初始索引。在 snapshot_7 中,第 14 行使用相同的函数来提取错误语句的起始索引。

snapshot_9
  在 snapshot_5 中,在第 15 行成功执行executeCreateOfferTx 函数之后,下一步涉及验证状态更改。为了实现这一点,在第 32 和 33 行中进行了断言。此外,在第 30 和 31 行中,调用了 getOfferId 和 getOfferDetails 函数,这些函数在内部使用脚本。

snapshot_10
  在上图中,很明显 getOfferId 和 getOfferDetails 函数利用了 scriptExecutor 函数,该函数使用硬编码的脚本路径以及必要的参数。然后,通过使用 as 和 ! 将脚本执行的结果强制转换为预期类型操作符。下面的快照显示了 scriptExecutor 辅助函数的实现。

snapshot_11
  该脚本使用 Test.readFile 读取并通过 Blockchain.executeScript 执行,这需要脚本代码和必要的参数。如果脚本抛出错误,则断言将失败。否则,将返回 scriptResult.returnValue。现在我们将使用以下命令运行我们构建的测试用例:
flow test -l "info,error" ./lib/cadence/test/Offers.cdc
返回输出应该是这样的

snapshot_12

写在最后

  Cadence 测试框架正处于开发的早期阶段,随着时间的推移,它可能会经历各种变化和改进。虽然它不一定是 Cadence 智能合约的最佳测试框架,但它是一个值得探索的快速选项。随着这个框架的不断发展,预计会看到更多的进步。


声明:本文为本站编辑转载,文章版权归原作者所有。文章内容为作者个人观点,本站只提供转载参考(依行业惯例严格标明出处和作译者),目的在于传递更多专业信息,普惠测试相关从业者,开源分享,推动行业交流和进步。 如涉及作品内容、版权和其它问题,请原作者及时与本站联系(QQ:1017718740),我们将第一时间进行处理。本站拥有对此声明的最终解释权!欢迎大家通过新浪微博(@测试窝)或微信公众号(测试窝)关注我们,与我们的编辑和其他窝友交流。
232° /2328 人阅读/0 条评论 发表评论

登录 后发表评论