本篇內(nèi)容介紹了“solidity測試用例分析”的有關(guān)知識,在實(shí)際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
網(wǎng)站建設(shè)哪家好,找成都創(chuàng)新互聯(lián)!專注于網(wǎng)頁設(shè)計(jì)、網(wǎng)站建設(shè)、微信開發(fā)、微信平臺小程序開發(fā)、集團(tuán)企業(yè)網(wǎng)站建設(shè)等服務(wù)項(xiàng)目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了海州免費(fèi)建站歡迎大家使用!
作為軟件開發(fā)者,我們都知道要讓代碼正常運(yùn)行,測試是非常重要的一個環(huán)節(jié)?;趨^(qū)塊鏈的去中心化軟件也不例外,而且由于區(qū)塊鏈的不可修改特性,測試對于區(qū)塊鏈軟件來說就更重要了。
總體上來說有兩種類型的軟件測試:單元測試和集成測試。單元測試聚焦于單個函數(shù)的測試,而集成測試的目標(biāo)則是確保各部分代碼組合起來也可以按期望的方式運(yùn)行。
Truffle是應(yīng)用最廣的以太坊智能合約與DApp開發(fā)框架,它提供了兩種用于測試以太坊智能合約的方法:Solidity測試和JavaScript測試。問題是,我們應(yīng)該選擇哪一種方法?
答案是都需要。
用Solidity編寫智能合約的測試用例讓我們可以在區(qū)塊鏈層級進(jìn)行測試。這種測試用例可以調(diào)用合約方法,就像用例部署在區(qū)塊鏈里一樣。為了測試智能合約的內(nèi)部行為,我們可以:
編寫Solidity單元測試來檢查智能合約函數(shù)的返回值以及狀態(tài)變量的值
編寫Solidity集成測試來檢查智能合約之間的交互。這些集成測試可以確保像繼承或者 依賴注入這樣的機(jī)制的運(yùn)行符合預(yù)期
我們也需要確保智能合約能夠表現(xiàn)出正確的外部行為。為了從區(qū)塊鏈外部測試智能合約,我們在JavaScript測試用例中使用web3.js,就像在開發(fā)DApp時一樣。我們需要對DApp前端可以正確調(diào)用智能合約建立信心。這方面的測試屬于集成測試。
因此,簡單地說,Solidity測試用例主要用于智能合約內(nèi)部實(shí)現(xiàn)邏輯的驗(yàn)證,可以用于單元測試和集成測試;而JavaScript用例則主要用于智能合約外部行為的驗(yàn)證,通常用于集成測試。
假設(shè)我們有兩個以太坊智能合約需要測試:Background和Entrypoint。
Background是一個內(nèi)部合約,我們的DApp前端不會直接和它交互。EntryPoint則是專門供DApp交互的智能合約,在EntryPoint合約內(nèi)部會訪問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合約提供了三個函數(shù):
storeValue(uint):寫入值
getValue(uint) :讀取值
getNumberOfValues():獲取值的數(shù)量
這三個合約函數(shù)都很簡單,因此也很容易進(jìn)行單元測試。
下面是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合約的構(gòu)造函數(shù)中,我們注入了Background合約的部署地址,并將其存入一個狀態(tài)變量backgroundAddress。EntryPoint合約暴露出三個函數(shù):
getBackgroundAddress():返回Background合約的部署地址
storeTwoValues(uint, uint):保存兩個值
getNumberOfValues():返回值的數(shù)量
由于storeTwoValues(uint, uint)函數(shù)兩次調(diào)用Background合約中的一個函數(shù),因此對這個函數(shù)進(jìn)行單元測試比較困難。getNumberOfValues()也有同樣的問題,因此這兩個函數(shù)更適合進(jìn)行集成測試。
在這一部分,我們學(xué)習(xí)如何為智能合約編寫Solidity單元測試用例和集成測試用例。讓我們先從簡單一點(diǎn)的單元測試開始。
下面是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數(shù)組中保存新的值
按索引返回values
在values數(shù)組中保存多個值
返回values數(shù)組的大小
下面的TestEntryPoint測試中包含了一個單元測試testItHasCorrectBackground() 用于驗(yàn)證EntryPoint合約的功能符合預(yù)期:
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"); } }
這個函數(shù)對依賴注入進(jìn)行測試。如前所述,EntryPoint合約中的其他函數(shù)需要與Background合約交互,因此我們沒有辦法單獨(dú)測試這些函數(shù),需要在集成測試中進(jìn)行驗(yàn)證。下面是集成測試的代碼:
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的擴(kuò)展,即定義在第43行的BackgroundTest,以其作為我們的模擬合約,這可以讓我們的測試用例檢查EntryPoint是否調(diào)用了部署在backgroundAddress地址處的合約的正確的函數(shù)。
我們用JavaScript編寫集成測試來確保合約的外部行為滿足預(yù)期要求,這樣我們就有信息基于這些智能合約開發(fā)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合約中的函數(shù),JavaScript測試可以確保我們可以將區(qū)塊鏈外部的值利用交易傳入智能合約,這是通過調(diào)用合約的storeTwoValues(uint,uint)函數(shù)(第15行)實(shí)現(xiàn)的。
當(dāng)談到智能合約的測試時,可以說越多越好,應(yīng)當(dāng)盡可能覆蓋所有可能的執(zhí)行路徑都返回預(yù)期的結(jié)果。Truffle提供了兩種辦法:區(qū)塊鏈層的Solidity單元測試和集成測試,以及DApp級別的JavaScript集成測試,我們在實(shí)際的工作中需要根據(jù)智能合約的代碼實(shí)現(xiàn)綜合運(yùn)用這兩種測試方法來保證智能合約的運(yùn)行符合預(yù)期。
“solidity測試用例分析”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!