扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
本篇内容介绍了“solidity测试用例分析”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
网站建设哪家好,找成都创新互联!专注于网页设计、网站建设、微信开发、微信平台小程序开发、集团企业网站建设等服务项目。为回馈新老客户创新互联还提供了海州免费建站欢迎大家使用!
作为软件开发者,我们都知道要让代码正常运行,测试是非常重要的一个环节。基于区块链的去中心化软件也不例外,而且由于区块链的不可修改特性,测试对于区块链软件来说就更重要了。
总体上来说有两种类型的软件测试:单元测试和集成测试。单元测试聚焦于单个函数的测试,而集成测试的目标则是确保各部分代码组合起来也可以按期望的方式运行。
Truffle是应用最广的以太坊智能合约与DApp开发框架,它提供了两种用于测试以太坊智能合约的方法:Solidity测试和JavaScript测试。问题是,我们应该选择哪一种方法?
答案是都需要。
用Solidity编写智能合约的测试用例让我们可以在区块链层级进行测试。这种测试用例可以调用合约方法,就像用例部署在区块链里一样。为了测试智能合约的内部行为,我们可以:
编写Solidity单元测试来检查智能合约函数的返回值以及状态变量的值
编写Solidity集成测试来检查智能合约之间的交互。这些集成测试可以确保像继承或者 依赖注入这样的机制的运行符合预期
我们也需要确保智能合约能够表现出正确的外部行为。为了从区块链外部测试智能合约,我们在JavaScript测试用例中使用web3.js,就像在开发DApp时一样。我们需要对DApp前端可以正确调用智能合约建立信心。这方面的测试属于集成测试。
因此,简单地说,Solidity测试用例主要用于智能合约内部实现逻辑的验证,可以用于单元测试和集成测试;而JavaScript用例则主要用于智能合约外部行为的验证,通常用于集成测试。
假设我们有两个以太坊智能合约需要测试:Background和Entrypoint。
Background是一个内部合约,我们的DApp前端不会直接和它交互。EntryPoint则是专门供DApp交互的智能合约,在EntryPoint合约内部会访问Background合约。
下面是Background合约的solidity代码:
pragma solidity >=0.5.0; contract Background { uint[] private values; function storeValue(uint value) public { values.push(value); } function getValue(uint initial) public view returns(uint) { return values[initial]; } function getNumberOfValues() public view returns(uint) { return values.length; } }
在上面,我们看到Background合约提供了三个函数:
storeValue(uint):写入值
getValue(uint) :读取值
getNumberOfValues():获取值的数量
这三个合约函数都很简单,因此也很容易进行单元测试。
下面是EntryPoint合约的Solidity代码:
pragma solidity >=0.5.0; import "./Background.sol"; contract EntryPoint { address public backgroundAddress; constructor(address _background) public{ backgroundAddress = _background; } function getBackgroundAddress() public view returns (address) { return backgroundAddress; } function storeTwoValues(uint first, uint second) public { Background(backgroundAddress).storeValue(first); Background(backgroundAddress).storeValue(second); } function getNumberOfValues() public view returns (uint) { return Background(backgroundAddress).getNumberOfValues(); } }
在EntryPoint合约的构造函数中,我们注入了Background合约的部署地址,并将其存入一个状态变量backgroundAddress。EntryPoint合约暴露出三个函数:
getBackgroundAddress():返回Background合约的部署地址
storeTwoValues(uint, uint):保存两个值
getNumberOfValues():返回值的数量
由于storeTwoValues(uint, uint)函数两次调用Background合约中的一个函数,因此对这个函数进行单元测试比较困难。getNumberOfValues()也有同样的问题,因此这两个函数更适合进行集成测试。
在这一部分,我们学习如何为智能合约编写Solidity单元测试用例和集成测试用例。让我们先从简单一点的单元测试开始。
下面是TestBackground测试的代码:
pragma solidity >=0.5.0; import "truffle/Assert.sol"; import "truffle/DeployedAddresses.sol"; import "../../../contracts/Background.sol"; contract TestBackground { Background public background; // Run before every test function function beforeEach() public { background = new Background(); } // Test that it stores a value correctly function testItStoresAValue() public { uint value = 5; background.storeValue(value); uint result = background.getValue(0); Assert.equal(result, value, "It should store the correct value"); } // Test that it gets the correct number of values function testItGetsCorrectNumberOfValues() public { background.storeValue(99); uint newSize = background.getNumberOfValues(); Assert.equal(newSize, 1, "It should increase the size"); } // Test that it stores multiple values correctly function testItStoresMultipleValues() public { for (uint8 i = 0; i < 10; i++) { uint value = i; background.storeValue(value); uint result = background.getValue(i); Assert.equal(result, value, "It should store the correct value for multiple values"); } } }
这个单元测试的目的是确保Background合约可以:
在values数组中保存新的值
按索引返回values
在values数组中保存多个值
返回values数组的大小
下面的TestEntryPoint测试中包含了一个单元测试testItHasCorrectBackground() 用于验证EntryPoint合约的功能符合预期:
pragma solidity >=0.5.0; import "truffle/Assert.sol"; import "truffle/DeployedAddresses.sol"; import "../../../contracts/Background.sol"; import "../../../contracts/EntryPoint.sol"; contract TestEntryPoint { // Ensure that dependency injection working correctly function testItHasCorrectBackground() public { Background backgroundTest = new Background(); EntryPoint entryPoint = new EntryPoint(address(backgroundTest)); address expected = address(backgroundTest); address target = entryPoint.getBackgroundAddress(); Assert.equal(target, expected, "It should set the correct background"); } }
这个函数对依赖注入进行测试。如前所述,EntryPoint合约中的其他函数需要与Background合约交互,因此我们没有办法单独测试这些函数,需要在集成测试中进行验证。下面是集成测试的代码:
pragma solidity >=0.5.0; import "truffle/Assert.sol"; import "truffle/DeployedAddresses.sol"; import "../../../contracts/Background.sol"; import "../../../contracts/EntryPoint.sol"; contract TestIntegrationEntryPoint { BackgroundTest public backgroundTest; EntryPoint public entryPoint; // Run before every test function function beforeEach() public { backgroundTest = new BackgroundTest(); entryPoint = new EntryPoint(address(backgroundTest)); } // Check that storeTwoValues() works correctly. // EntryPoint contract should call background.storeValue() // so we use our mock extension BackgroundTest contract to // check that the integration workds function testItStoresTwoValues() public { uint value1 = 5; uint value2 = 20; entryPoint.storeTwoValues(value1, value2); uint result1 = backgroundTest.values(0); uint result2 = backgroundTest.values(1); Assert.equal(result1, value1, "Value 1 should be correct"); Assert.equal(result2, value2, "Value 2 should be correct"); } // Check that entry point calls our mock extension correctly // indicating that the integration between contracts is working function testItCallsGetNumberOfValuesFromBackground() public { uint result = entryPoint.getNumberOfValues(); Assert.equal(result, 999, "It should call getNumberOfValues"); } } // Extended from Background because values is private in actual Background // but we're not testing background in this unit test contract BackgroundTest is Background { uint[] public values; function storeValue(uint value) public { values.push(value); } function getNumberOfValues() public view returns(uint) { return 999; } }
我们可以看到TestIntegrationEntryPoint使用了一个Background的扩展,即定义在第43行的BackgroundTest,以其作为我们的模拟合约,这可以让我们的测试用例检查EntryPoint是否调用了部署在backgroundAddress地址处的合约的正确的函数。
我们用JavaScript编写集成测试来确保合约的外部行为满足预期要求,这样我们就有信息基于这些智能合约开发DApp了。
下面是我们的JavaScript测试文件entryPoint.test.js:
const EntryPoint = artifacts.require("./EntryPoint.sol"); require('chai') .use(require('chai-as-promised')) .should(); contract("EntryPoint", accounts => { describe("Storing Values", () => { it("Stores correctly", async () => { const entryPoint = await EntryPoint.deployed(); let numberOfValues = await entryPoint.getNumberOfValues(); numberOfValues.toString().should.equal("0"); await entryPoint.storeTwoValues(2,4); numberOfValues = await entryPoint.getNumberOfValues(); numberOfValues.toString().should.equal("2"); }); }); });
使用EntryPoint合约中的函数,JavaScript测试可以确保我们可以将区块链外部的值利用交易传入智能合约,这是通过调用合约的storeTwoValues(uint,uint)函数(第15行)实现的。
当谈到智能合约的测试时,可以说越多越好,应当尽可能覆盖所有可能的执行路径都返回预期的结果。Truffle提供了两种办法:区块链层的Solidity单元测试和集成测试,以及DApp级别的JavaScript集成测试,我们在实际的工作中需要根据智能合约的代码实现综合运用这两种测试方法来保证智能合约的运行符合预期。
“solidity测试用例分析”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注创新互联网站,小编将为大家输出更多高质量的实用文章!
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流