Solidity 测试内部函数

文章介绍了如何通过创建子合约来测试Solidity中的内部函数,并解释为什么不应将函数改为public或virtual。同时还提到了无法测试private函数的原因,并提供了相关代码示例。

要测试一个内部的 Solidity 函数,创建一个继承被测试合约的子合约,用一个外部函数包装父合约的内部函数,然后在子合约中测试这个外部函数。

Foundry 将这一继承合约称为“harness”,而其他人则称之为“fixture”。

不要将函数更改为 virtual 或 public 来方便扩展,你需要测试的是你将实际部署的合约。

以下是一个示例。

contract InternalFunction {

    function calculateReward(uint256 depositTime) **internal** view returns (uint256 reward) {
        reward = (block.timestamp - depositTime) * REWARD_RATE_PER_SECOND;
    }
}

上面的函数为每单位时间的流逝提供了线性奖励率。

fixture(或 harness)看起来像这样:

contract InternalFunctionHarness is InternalFunction {

    function calculateReward(uint256 depositTime) **external** view returns (uint256 reward) {
        reward = super.calculateReward(depositTime);
    }
}

当你调用一个与子合约同名的父合约函数时,必须使用 super 关键字,否则函数会自我调用并进入无限递归。

另外,你可以明确将你的测试函数标记为 harness 或 fixture,如下所示:

contract InternalFunctionHarness is InternalFunction {

    function calculateReward_HARNESS(uint256 depositTime) **external** view returns (uint256 reward) {
        reward = calculateReward(depositTime);
    }
}

不要将函数更改为 public

将函数更改为 public 不是一个好的解决方案,因为这将增加合约大小。如果一个函数不需要是 public,那就不要将其设为 public。这会增加部署和其他函数执行的 gas 成本。

当一个 合约 接收交易时,它必须将 函数选择器 与所有 public 函数进行线性或二分搜索。无论哪种情况,它都必须搜索更多的选择器。 此外,添加的选择器会增加字节码,从而增加部署成本。

不要重写 virtual 函数

假设我们有以下合约:

contract InternalFunction {

    function calculateReward(uint256 depositTime) **internal** view virtual returns (uint256 reward) {
        reward = (block.timestamp - depositTime) * REWARD_RATE_PER_SECOND;
    }
}

在 fixture 中简单地重写它可能会很诱人以方便使用,但这并不可取,因为这样会导致代码重复,如果你的 harness 实现与父合约不一致,你就不会真正测试你的业务逻辑了。

请注意,这种方法迫使我们复制和粘贴原始代码:

contract InternalFunctionHarness in InternalFunction {

    function calculateReward(uint256 depositTime) **external** view override returns (uint256 reward) {
        reward = (block.timestamp - depositTime) * REWARD_RATE_PER_SECOND;
    }
}

测试私有 Solidity 函数怎么办?

在 Solidity 中没有办法测试私有函数,因为它们在子合约中不可见。内部函数与私有函数之间的区别在合约编译后不存在。因此,你可以将私有函数更改为内部函数,而不会对 gas 成本产生负面影响。

作为读者的练习,基准测试以下代码,看看将 foo 改为内部是否会影响 gas 成本。

contract A {
    // 将此更改为私有
    function foo() **internal** pure returns (uint256 f) {
        f = 2;
    }

    function bar() **internal** pure returns (uint256 b) {
        b = foo();
    }
}

contract B is A {
    // 146 gas: 0.8.7 no optimizer
    function baz() **external** pure returns (uint256 b) {
        b = bar();
    }
}

了解更多

请参阅我们的高级 solidity bootcamp 学习更高级的测试方法。

我们还有一个免费的 solidity 教程 帮助你入门。

最初发布于 2023 年 4 月 6 日

  • 原文链接: rareskills.io/post/solid...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
RareSkills
RareSkills
https://www.rareskills.io/