在智能合约开发方面,良好的单元测试至关重要。事实上,鉴于智能合约是不可篡改的,并且通常负责管理大笔资金,对智能合约开发良好的单元测试比传统的网络和移动应用程序更重要。
在过去的两个月里,我们非常幸运地与一家金融服务公司密切合作,该公司正在开发一种创新的平台,以符合法规的方式对证券进行标记。他们即将发布的平台将引入将数十亿美元的证券转移到区块链的能力。
为了支持他们的发布,我们与他们的工程团队密切合作,实施了全面的测试覆盖,并针对我们的错误发现过程执行。根据我们的经验,我们提出了一些关键考虑因素,以及在为智能合约开发单元测试时可以使用的一些提示和技巧。
获取对业务逻辑的正确理解
首先要确保你对分布式应用程序(dapp)业务逻辑有一个全面的了解。
记录你的dapp将解决的问题以及如何解决这些问题是有帮助的。你还需要确定你的dapp具有的不同约束。例如,大多数dapps都会采用某种形式的基于角色的访问控制或基于合约的访问控制。记录这些约束及其对使用的影响非常重要。
不仅如此,你还需要转录dapp的工作流程。是否要求某些合约在其他合约之前被激活?特定合约是否应暂停或取消暂停以完成特定任务?这些是业务逻辑和工作流相关要求的类型,在你深入研究并开始开发单元测试之前,应该很好地理解这些要求。
绘制单元测试
在你记录了dapp的业务逻辑和工作流程之后,我们建议你绘制与dapp相应功能相关的每个单元测试。这要求你按照功能划分,逐个合约和逐个功能,最终指出与每个潜在用例相关的所有相应单元测试。
为此,我们将单元测试组合在一起,以便将它们映射到合约中的相应函数。 以下是与令牌合约中的buyTokens(_amount)函数关联的单个用例的示例规范:
相反,在提供期结束时,这是与buyTokens(_amount)函数相关的另一个规范:
绘制出所有这样的单元测试,是促进与利益相关者和其他工程团队进行对话的好方法。 在达到dapp的要求时,它有助于确保每个人都在同一页面上。不仅如此,它还有助于指导你如何以安全可靠的方式构建和建立你的dapp。
首先解决修饰符,然后按顺序处理require和if语句。
在绘制出单元测试之后,我们建议你首先关注与函数修饰符关联的用例。然后,你可以按顺序逐步执行该函数并开发单元测试,以解决与每个require和if语句关联的所有用例。
例如,对于像下面这样的函数,我们首先要解决与atStage(Stages.AuctionStarted)和validBid()修饰符相关的用例,然后再解决后面的if和else语句:
为了测试合约何时还原,我们发现OpenZeppelin的assertRevert助手非常有用。 你可以像这样使用它:
函数重载和低级调用的需要
随着你继续开发单元测试,你可能会遇到与你正在使用的开发人员工具相关的缺点。这是因为智能合约开发空间仍然很新,因此许多开发人员工具仍然不成熟。
例如,Truffle框架 – 这是一个优秀的工具,也许是今天以太坊空间中最流行的框架 – 不支持函数重载。也就是说,如果你需要测试两个具有相同名称但不同arities的函数,你需要使用低级调用来测试合约ABI中的第二个函数。如果不这样做,你将收到错误提示:Error: Invalid number of arguments to Solidity function
我们来看一个简单的例子吧。
如果你在合约中有两个buyTokens函数,一个不带参数并且在ABI中首先列出,一个接受(_amount)参数并且在ABI中列为第二个,你需要使用低级调用 使用encodeCall测试buyTokens(_amount)函数,如下所示。
Truffle社区已经意识到这个问题,它将在即将发布的版本中得到解决。然而,当你开发智能合约及其相应的单元测试时,这样的古怪场景比较常见。使用你常常调用的解决方案时,你将不得不变得非常小心。
如何测试内部功能
鉴于Solidity中的函数可以具有不同的可见性(即public,private,internal和external),值得注意的是,开发内部函数的单元测试与为公共函数开发它们略有不同。这是因为内部函数在编译后未在智能合约的ABI中列出。因此,为了测试内部函数,你有两个选择:
你可以创建另一个继承你正在测试的合约,以便测试内部函数
或者,你可以在调用内部函数的契约中的其他函数内测试内部函数的逻辑
这两种方法都可以完成工作,尽管有些人可能会认为从合约中的其他函数中测试内部函数的逻辑更直观。
方便的Ganache提示和技巧
正如我之前提到的,我们主要使用Truffle框架为我们的客户构建dapps。Truffle使用一种名为Ganache的工具,它可以让你快速启动自己的个人以太坊区块链,你可以使用它来运行测试,执行命令和检查状态,同时控制链的运行方式。它超级方便。
Ganache的真正优点在于它可以轻松定制以满足你对dapp的独特需求。例如,我们为dapps开发了单元测试,要求我们为数十个用户测试用例。通过一个简单的命令,Ganache使我们可以轻松地使用我们需要的帐户进行测试:
此外,我们可以将这些帐户的余额设置为我们的测试所需的ETH。默认情况下,Truffle将余额设置为100 ETH。我们可以根据自己的独特要求轻松增加或减少该值:
在为以太坊开发智能合约时,Ganache是一个非常有用的工具。它有助于简化开发,降低风险并提高dapp可靠性。
将它们捆绑在一起
智能合约空间中的单元测试基本上类似于Web和移动应用程序领域中的单元测试。最大的区别在于,今天的智能合约是不可变的,并且通常不会提供简单的方法来随着时间的推移迭代它们。因此,鉴于智能合约的不变性以及其通常负责管理大笔资金的事实,开发良好的单元测试至关重要。仔细测试时确保智能合约安全可靠的唯一方法。
为了大规模地应用智能合约,社区必须共同努力推进,并使开发人员更容易在公共区块链上编写复杂的应用程序。
我们希望从开发单元测试的经验中学到的这些经验教训是有用的,并支持你建立安全可靠的智能合同的努力。如果您有其他问题或想法想与我们的团队进一步讨论,请随时联系我们——team@upstate.agency。
本文由以太中文网翻译。原文链接:blog.upstate.agency/a-simple-gu…