Java开发的智能合约单元测试教程

  • 时间:
  • 浏览:70
  • 来源:区块链技术网

在本教程中,我们将实现一个简单的合约,为其编写单元测试,并运行调试过程以查找Bug。运行该示例所需的全部是Java和IDE。 只需创建一个依赖AVM最新工具库的Java项目作为库。

1.编写智能合约首先创建智能合约。以下是可用于简单投票DApp的智能合约的示例。智能合约的功能:

1. 只有合约所有者(部署合约的帐户)可以添加或删除成员。

2. 只有会员可以介绍新的提案。

3. 只有会员可以对提案进行投票。

4. 如果超过50%的成员赞成该提案并对其投票,该提案将通过。

packageorg.aion.avm.embed.temp;importavm.Blockchain;importorg.aion.avm.tooling.abi.Callable;importorg.aion.avm.userlib.AionMap;importorg.aion.avm.userlib.AionSet;importavm.Address;importorg.aion.avm.userlib.abi.ABIDecoder;publicclassVoting{privatestaticAionSet<Address>members=newAionSet<>();privatestaticAionMap<Integer,

Proposal>proposals=newAionMap<>();privatestaticAddressowner;privatestaticintminimumQuorum;static{ABIDecoderdecoder=newABIDecoder(Blockchain.getData());Address[]arg=decoder.decodeOneAddressArray();for(Addressaddr:arg){members.add(addr);}updateQuorum();owner=Blockchain.getCaller();}@CallablepublicstaticvoidaddProposal(Stringdescription){Addresscaller=Blockchain.getCaller();Blockchain.require(isMember(caller));Proposalproposal=newProposal(description,caller);intproposalId=proposals.size();proposals.put(proposalId,proposal);Blockchain.log("NewProposalAdded".getBytes(),

Integer.toString(proposalId).getBytes(),caller.toByteArray(),

description.getBytes());}@Callablepublicstaticvoidvote(intproposalId){Addresscaller=Blockchain.getCaller();Blockchain.require(isMember(caller)&&proposals.containsKey(

proposalId));ProposalvotedProposal=proposals.get(proposalId);votedProposal.voters.add(caller);Blockchain.log("Voted".getBytes(),

Integer.toString(proposalId).getBytes(),caller.toByteArray());if(!votedProposal.isPassed&&votedProposal.voters.size()==

minimumQuorum){votedProposal.isPassed=true;Blockchain.log("ProposalPassed".getBytes(),

Integer.toString(proposalId).getBytes());}}@CallablepublicstaticvoidaddMember(AddressnewMember){onlyOwner();members.add(newMember);updateQuorum();Blockchain.log("MemberAdded".getBytes(),

newMember.toByteArray());}@CallablepublicstaticvoidremoveMember(Addressmember){onlyOwner();members.remove(member);updateQuorum();Blockchain.log("MemberRemoved".getBytes(),

member.toByteArray());}@CallablepublicstaticStringgetProposalDescription(intproposalId){returnproposals.containsKey(proposalId)?

proposals.get(proposalId).description:null;}@CallablepublicstaticAddressgetProposalOwner(intproposalId){returnproposals.containsKey(proposalId)?

proposals.get(proposalId).owner:null;}@CallablepublicstaticbooleanhasProposalPassed(intproposalId){returnproposals.containsKey(proposalId)&&

proposals.get(proposalId).isPassed;}@CallablepublicstaticintgetMinimumQuorum(){returnminimumQuorum;}@CallablepublicstaticbooleanisMember(Addressaddress){returnmembers.contains(address);}privatestaticvoidonlyOwner(){Blockchain.require(owner.equals(Blockchain.getCaller()));}privatestaticvoidupdateQuorum(){minimumQuorum=members.size()/2+1;}privatestaticclassProposal{Stringdescription;Addressowner;booleanisPassed;AionSet<Address>voters=newAionSet<>();Proposal(Stringdescription,Addressowner){this.description=description;this.owner=owner;}}}合约中的static块在部署时仅执行一次。 我们在此块中设置初始成员,minimumQuorum和所有者。尽管我们与一组成员启动了合约,但所有者随后也可以添加和删除成员。我们使用AionSet和AionMap跟踪成员及其提案。可以使用其唯一标识符从地图访问每个建议。智能合约的主要职能是:

addProposal,允许成员添加提案说明以进行投票。

vote,允许成员通过传递其ID对可用提案进行投票。赢得多数成员投票的提案将通过。请注意,将生成ProposalPassed事件以记录已通过投标的ID。

2.编写单元测试我们将使用AvmRule编写测试。 AvmRule是用于在嵌入式AVM上测试合约的Junit规则。 它创建Aion内核和AVM的内存中表示形式。每次我们运行测试时,都会刷新内置的内核和AVM实例。在测试我们的合约之前,我们需要将其部署到内存中的Aion区块链中,并且我们将使用AvmRule完成此任务。A.实例化AvmRule您可以看到该规则包含一个布尔参数,该布尔参数启用/禁用调试模式。 最好在启用调试器的情况下编写测试。您可以在下一部分中查看如何调试合约。

@RulepublicAvmRuleavmRule=newAvmRule(true);注意:此行将为每种测试方法实例化嵌入式AVM。如果将规则定义为@classRule,则将仅为测试类实例化AVM和内核的一个实例。B.获取合约字节现在,我们必须获取与合约jar的内存表示相对应的字节。为了获取字节,我们将使用AvmRule中的getDappBytes方法。getDappBytes采用以下参数:1. 合约的主要类别。2. 合约构造函数参数,可以在静态块中对其进行访问和解码。3. DApp jar中需要包含其他类。

publicbyte[]getDappBytes(Class<?>mainClass,byte[]arguments,Class<?>...otherClasses)C.部署您的智能合约使用部署功能可以轻松完成智能合约的部署。
publicResultWrapperdeploy(Addressfrom,BigIntegervalue,byte[]dappBytes)AvmRule还提供了在Aion内核中创建具有初始余额的帐户的功能。以下是由3名成员组成的小组部署投票智能合约的方法。

publicclassVotingContractTest{@RulepublicAvmRuleavmRule=newAvmRule(true);publicAddressdappAddress;publicAddressowner=avmRule.getPreminedAccount();publicAddress[]members=newAddress[3];@Beforepublicvoidsetup(){for(inti=0;i<members.length;i++){//createaccountswithinitialbalancemembers[i]=avmRule.getRandomAddress(

BigInteger.valueOf(10_000_000L));}//encodemembersarrayasanargumentbyte[]deployArgument=ABIUtil.encodeOneObject(members);//getthebytesrepresentingtheinmemoryjarbyte[]dappBytes=avmRule.getDappBytes(

Voting.class,deployArgument);//deployandgetthecontractaddressdappAddress=avmRule.deploy(

owner,BigInteger.ZERO,dappBytes).getDappAddress();}}D.调用方法我们可以通过以下方式调用合同中的方法:1. 编码方法名称及其参数。2. 将编码后的字节传递给AvmRule的call方法。

publicResultWrappercall(Addressfrom,AddressdappAddress,BigIntegervalue,byte[]transactionData)例如我们创建一个新的提案。我们将通过检查是否生成了NewProposalAdded事件以及事件主题和数据正确来验证提案。

@TestpublicvoidaddProposalTest(){Stringdescription="newproposaldescription";byte[]txData=ABIUtil.encodeMethodArguments(

"addProposal",description);AvmRule.ResultWrapperresult=avmRule.call(

members[0],dappAddress,BigInteger.ZERO,txData);//assertthetransactionwassuccessfulAssert.assertTrue(result.getReceiptStatus().isSuccess());//asserttheeventisgeneratedAssert.assertEquals(1,result.getLogs().size());Loglog=result.getLogs().get(0);//validatethetopicsanddataAssert.assertArrayEquals(LogSizeUtils.truncatePadTopic(

"NewProposalAdded".getBytes()),log.copyOfTopics().get(0));Assert.assertArrayEquals(LogSizeUtils.truncatePadTopic(

Integer.toString(0).getBytes()),log.copyOfTopics().get(1));Assert.assertArrayEquals(LogSizeUtils.truncatePadTopic(

members[0].toByteArray()),log.copyOfTopics().get(2));Assert.assertArrayEquals(description.getBytes(),log.copyOfData());}

现在我们将提交一个提案以及两个投票。由于有两个不同的成员以ID 0对提案进行了投票,提案应通过。因此我们希望为最后一个事务生成两个不同的事件-Voted和ProposalPassed。您还可以通过提案ID查询提案的状态。您会看到返回的解码数据为true,表示提案已通过。

@TestpublicvoidvoteTest(){Stringdescription="newproposaldescription";byte[]txData=ABIUtil.encodeMethodArguments(

"addProposal",description);AvmRule.ResultWrapperresult=avmRule.call(

members[0],dappAddress,BigInteger.ZERO,txData);Assert.assertTrue(result.getReceiptStatus().isSuccess());Assert.assertEquals(1,result.getLogs().size());//vote#1txData=ABIUtil.encodeMethodArguments("vote",0);result=avmRule.call(

members[1],dappAddress,BigInteger.ZERO,txData);Assert.assertTrue(result.getReceiptStatus().isSuccess());Assert.assertEquals(1,result.getLogs().size());//vote#2txData=ABIUtil.encodeMethodArguments("vote",0);result=avmRule.call(

members[2],dappAddress,BigInteger.ZERO,txData);Assert.assertTrue(result.getReceiptStatus().isSuccess());Assert.assertEquals(2,result.getLogs().size());//validatethattheproposalisstoredaspassedtxData=ABIUtil.encodeMethodArguments("hasProposalPassed",0);result=avmRule.call(

members[2],dappAddress,BigInteger.ZERO,txData);//decodethereturndataasbooleanAssert.assertTrue((boolean)result.getDecodedReturnData());}

3.调试智能合约调试我们的智能合约非常容易,只需在源代码中设置断点即可!由于我们在启用调试的情况下创建了AvmRule,因此在达到断点时将停止执行。让我们来看一个例子。这是部署后智能合约的状态。

您可以看到该智能合约具有:1. 3名成员。2. 0个提案。3. minimumQuorum = 2。您也可以检查每个集合的内容。例如通过调用addProposal,您将能够看到更新的AionMap。

让我们实际对调试器进行测试。我们将故意在评估提案通过方式时造成一个简单的错误。我将修改提案通过条件,如下所示。 请注意,等于条件已更改为小于或等于。

if(!votedProposal.isPassed&&votedProposal.voters.size()<=minimumQuorum)现在当第一个所有者提交投票时,提案将通过。让我们调试方法调用并逐步执行该函数。

您可以看到,尽管minimumQuorum等于2,但是该提案的投票者计数仅为1。我们修改了if语句(来自上面),并且第51行的isPassed标志设置为true。从那里,您可以轻松地确定错误在代码中的位置。

结论如果您曾经为以太坊开发过智能合约,就会知道用一种陌生的专用语言编写合约并对其进行调试的痛苦。任何熟悉Java的人都会感觉像在家中一样使用AVM编写智能合约。另外市场上任何IDE中的所有调试功能都可用于测试和调试Java智能合约。

猜你喜欢

区块链红利吃饱后,这个巨头又想"征服"元宇宙?

据12月26日消息,百度与英伟达(NVIDIA)已达成协议,双方合作共建AI元宇宙。另外,在今日举行的百度AI开发者大会上,英伟达全球副总裁暨亚太区总裁 Raymond Teh将受邀出席,并发表主题演讲。

2021-12-27

2021年,区块链股权融资发生了怎么样的演变

过去一年,区块链行业融资井喷,在科技领域中独树一帜,A16z、红杉、老虎基金等等这些顶级机构在 2021 年的区块链行业肆意驰骋,在 DeFi、NFT、Metaverse 等领域扶持了一众创业项目。

2021-12-23

两个元宇宙的世界观,以及和区块链的关系

“元宇宙”这个名词音好听但义很难传达准确,想要更准确地理解义,取名为平行宇宙、竞争宇宙、山寨宇宙,更好。对应的,我们现在肉身所处的宇宙,我们称之为“肉身宇宙”。

2021-12-22

Alien Worlds(外星世界)区块链打金挖矿指南

Alien Worlds(外星世界)是一款4月就开始的游戏,10月27日又上线了全新的任务(Missions)游戏模式; 在WAX用户量排名中Alien Worlds(外星世界)一直排在前三,说明现在存量工作室还有很多;

2021-12-17

DeHorizon(地平线)区块链打金教程

今天我们就解析在一款社交主题游戏 — DeHorizon

2021-12-17