{"content":{"title":"使用hardhat 开发以太坊智能合约-测试合约","body":"> 本系列课程：\r\n> \r\n> 第一节：[使用hardhat开发以太坊智能合约-搭建环境](https://learnblockchain.cn/article/4885)\r\n> \r\n> 第二节：[使用hardhat开发以太坊智能合约-测试合约](https://learnblockchain.cn/article/4930)\r\n> \r\n> 第三节：[使用hardhat开发以太坊智能合约-发布合约](https://learnblockchain.cn/article/4929)\r\n> \r\n> 第四节：[使用hardhat开发以太坊智能合约-验证合约](https://learnblockchain.cn/article/4931)\r\n\r\n\r\n> 上一章我们讲解了如何搭建基本的hardhat开发环境，这一章我们来讲一下如何测试、部署合约\r\n\r\n\r\n> 特别提醒：本系列教程默认你已经掌握了solidity的开发技能，如果你对solidity还不能完全理解，请先移步solidity的相关教程 \r\n\r\n# 1、编译合约 \r\n我们使用vscode打开项目，在菜单左侧，打开contracts目录，可以发现里面已经有一个Lock.sol文件了，这个是hardhat为我们准备的测试的合约文件\r\n\r\n\r\n![image.png](https://img.learnblockchain.cn/attachments/2022/10/V0D4Hrc963564e4c63881.png)\r\n\r\n 打开Lock.sol文件\r\n\r\n![image.png](https://img.learnblockchain.cn/attachments/2022/10/7JmVj9ph63564e5380505.png)\r\n\r\n\r\n可以看到其实是一个很简单的逻辑，里面定义了2个变量，还有一个事件，以及一个调用方法，整个合约要实现的就是当事件满足一定条件的时候才可以从合约账号提现\r\n\r\n下面，我们使用hardhat提供的命令对合约文件进行编译：\r\n\r\n\r\n```js\r\nnpx hardhat compile\r\n```\r\n hardhat会帮我们编译contracts目录下面的所有合约文件\r\n\r\n![image.png](https://img.learnblockchain.cn/attachments/2022/10/ZjNW6m3X63564e632fb79.png)\r\n\r\n\r\n这时候我们可以看到在控制台已经输出了编译日志，如果编译不成功会提示相应的错误信息\r\n\r\n \r\n![image.png](https://img.learnblockchain.cn/attachments/2022/10/tZcErVwA63564e6adf5a1.png)\r\n\r\n 在我们的项目目录里同时也会生成一些新的文件，如上图所示，其中cache目录为缓存文件，artifacts目录下的build-info目录，存放的是构建项目的信息，contracts下面存放的是编译的每个合约对应的abi接口信息\r\n\r\n# 2、测试合约\r\n> 通过上面的操作，我们已经成功的编译了合约文件，下面我们来对合约文件进行测试，一个好的测试流程可以避免我们的合约出现一些严重的bug，特别是在区块链上，一旦合约部署上去就无法再次更新，所以我们在正式上线之前，必须将合约完全测试一遍\r\n\r\n## 2.1 测试脚本介绍 \r\n我们回到vscode，打开项目目录test，这里已经有了一个名为Lock.js的脚本文件\r\n\r\n![image.png](https://img.learnblockchain.cn/attachments/2022/10/JKuNGJl063564e83d4f7f.png)\r\n\r\n\r\n让我们打开它，看一下它的主要构成 \r\n\r\n![image.png](https://img.learnblockchain.cn/attachments/2022/10/wQKKCky463564e89ada93.png)\r\n\r\n\r\n 代码前6行定义了几个常用的测试辅助对象，其中 loadFixture，可以让我们在测试中都使用相同的配置，最常用的就是我们在测试类里会定义一个部署合约的方法，然后在需要使用合约对象的地方，通过loadFixture(function) 获取部署合约的快照对象\r\n\r\n在hardhat里，我们使用chaijs [Chaijs官网](https://www.chaijs.com/) 来进行断言测试， 感兴趣的朋友，可以浏览chai官网获取更多信息，通过使用expect方法，进行断言测试\r\n\r\n```js\r\ndescribe(\"Lock\", function () {})\r\n```\r\n\r\n这段代码就是标识测试的开始，其中第一个参数为测试标题，可以随便定义，第二个参数为要执行的函数体，我们在函数体里，进行具体测试逻辑的编写\r\n\r\n> describe是可以嵌套使用的\r\n\r\n所以我们一般测试会在第一个describe里定义一个合约部署方法，然后在describe里嵌套 describe，通过loadFixture(function) 获取相同合约部署快照\r\n\r\n## 2.2 合约部署方法\r\n回到刚刚的测试文件，我们继续阅读代码\r\n\r\n```js\r\nasync function deployOneYearLockFixture() {\r\n    const ONE_YEAR_IN_SECS = 365 * 24 * 60 * 60;\r\n    const ONE_GWEI = 1_000_000_000;\r\n \r\n    const lockedAmount = ONE_GWEI;\r\n    const unlockTime = (await time.latest()) + ONE_YEAR_IN_SECS;\r\n \r\n    // Contracts are deployed using the first signer/account by default\r\n    const [owner, otherAccount] = await ethers.getSigners();\r\n \r\n    const Lock = await ethers.getContractFactory(\"Lock\");\r\n    const lock = await Lock.deploy(unlockTime, { value: lockedAmount });\r\n \r\n    return { lock, unlockTime, lockedAmount, owner, otherAccount };\r\n  }\r\n```\r\n 这部分代码定义了一个合约部署方法，其中\r\n\r\n 第一个owner 即为我们部署合约的账号，可以通过加入更多字段接受返回，获取其他的账号信息，如otherAccount这个定义\r\n\r\n```js\r\n   const [owner, otherAccount] = await ethers.getSigners();\r\n   const Lock = await ethers.getContractFactory(\"Lock\");\r\n```\r\n\r\n这句代码创建了一个合约对象Lock，注意括号内的Lock对应的是合约文件内定义的名称，即\r\n\r\n\r\n```js\r\ncontract Lock {}\r\n```\r\n**请不要错把合约文件名当成该参数，否则会报错**\r\n\r\n\r\n```js\r\nconst lock = await Lock.deploy(unlockTime, { value: lockedAmount });\r\n```\r\n通过deploy方法进行合约部署，括号内为合约部署时所需的初始化值，如果合约不需要可以不填写\r\n\r\n##  2.3 测试方法\r\n\r\n```js\r\n describe(\"Deployment\", function () {\r\n    it(\"Should set the right unlockTime\", async function () {\r\n      const { lock, unlockTime } = await loadFixture(deployOneYearLockFixture);\r\n \r\n      expect(await lock.unlockTime()).to.equal(unlockTime);\r\n    });\r\n  });\r\n```\r\n 在describe里，使用 it进行测试，第一个参数为测试描述，第二个参数为具体方法实现\r\n\r\n\r\n```js\r\nconst { lock, unlockTime } = await loadFixture(deployOneYearLockFixture);\r\n```\r\n获取合约创建快照对象，从对象中获取到合约对象以及合约部署方法里定义的解锁时间\r\n\r\n\r\n```js\r\n expect(await lock.unlockTime()).to.equal(unlockTime);\r\n```\r\n使用expect进行断言，判断合约对象里面存储的解锁时间是否与合约部署方法里定义的解锁时间相等\r\n\r\n## 2.4 测试演示\r\n接下来，让我们运行测试，来看一下测试的结果\r\n\r\n使用如下命令来进行合约测试：\r\n\r\n\r\n```js\r\nnpx hardhat test\r\n```\r\n\r\n![image.png](https://img.learnblockchain.cn/attachments/2022/10/nlm0NbH063564f392f831.png)\r\n\r\n 从图中打印的信息我们可以清晰的看出测试结果，值得注意点是，打印也是具有层级的，这个是因为我们循环嵌套使用describe产生的效果，通过嵌套使用，可以让我们测试结果变得更为清晰，推荐大家使用\r\n\r\n> 通过本章的学习，我们了解了合约是如何进行测试的，以及测试需要注意的一些事项，接下来我们会讲解如何进行合约的部署，以及网络的选择，小伙伴们我们下节课见咯\r\n>"},"author":{"user":"https://learnblockchain.cn/people/5344","address":null},"history":"QmaCN9vVyiBxnuPSCmFQs8Bwd9NNJj9wAwSrzqC3f6VFny","timestamp":1666685437,"version":1}