Hardhat简单使用

265 阅读5分钟

编写一个合约,并用hardhat完成测试

合约开发完成后,需要进行手动测试,此时hardhat就能很方便我们对合约进行整体测试。

1.工作目录

创建好hardhat后,工作目录如下

Image.png

2.合约样例

这里我们用一个简单的合约样例来进行测试,代码如下:

// Solidity files have to start with this pragma.

// It will be used by the Solidity compiler to validate its version.

pragma solidity ^0.7.0;

  


  


// This is the main building block for smart contracts.

contract Token {

// Some string type variables to identify the token.

string public name = "My Hardhat Token";

string public symbol = "MBT";

  


// 固定发行量

uint256 public totalSupply = 1000000;

  


// An address type variable is used to store ethereum accounts.

address public owner;

  


// A mapping is a key/value map. Here we store each account balance.

mapping(address => uint256) balances;

  


/**

* 合约构造函数

*

* The `constructor` is executed only once when the contract is created.

* The `public` modifier makes a function callable from outside the contract.

*/

constructor() public {

// The totalSupply is assigned to transaction sender, which is the account

// that is deploying the contract.

balances[msg.sender] = totalSupply;

owner = msg.sender;

}

  


/**

* 代币转账.

*

* The `external` modifier makes a function *only* callable from outside

* the contract.

*/

function transfer(address to, uint256 amount) external {

// Check if the transaction sender has enough tokens.

// If `require`'s first argument evaluates to `false` then the

// transaction will revert.

require(balances[msg.sender] >= amount, "Not enough tokens");

  


// Transfer the amount.

balances[msg.sender] -= amount;

balances[to] += amount;

}

  


/**

* 读取某账号的代币余额

*

* The `view` modifier indicates that it doesn't modify the contract's

* state, which allows us to call it without executing a transaction.

*/

function balanceOf(address account) external view returns (uint256) {

return balances[account];

}

}

3.使用编译命令

$ npx hardhat compile

4.编写测试用例

我们通过一个简单的测试来介绍一下各行代码的作用,如下:

const { expect } = require("chai");

  


describe("Token contract", function() {

it("Deployment should assign the total supply of tokens to the owner", async function() {

const [owner] = await ethers.getSigners();

  


const Token = await ethers.getContractFactory("Token");

  


const hardhatToken = await Token.deploy();

await hardhatToken.deployed();

  


const ownerBalance = await hardhatToken.balanceOf(owner.getAddress());

expect(await hardhatToken.totalSupply()).to.equal(ownerBalance);

});

});
1.我们使用Chai,这是一个断言库。 这些断言函数称为“匹配器”,在此实际上使用的“匹配器”来自Waffle。这里的chai可以理解为java里面的工具库

const { expect } = require("chai");

2.描述某个测试函数,后面在测试的时候会在控制台输出字符串“Token contract”,第二个参数function内部写具体测试代码。

describe("Token contract", function() )

3.可以理解为测试某个功能,前一个函数代表整个测试用例,内部的函数为待测试的每个具体功能。同样的,第一个参数在测试的时候会输出该字符串,打勾即代表测试正常。

it("Deployment should assign the total supply of tokens to the owner", async function()

4.ethers.js中的Signer 代表以太坊账户对象。 它用于将交易发送到合约和其他帐户。 在这里,我们获得了所连接节点中的帐户列表,在本例中节点为Hardhat Network,并且仅保留第一个帐户。

const [owner] = await ethers.getSigners();

5.ethers.js中的ContractFactory是用于部署新智能合约的抽象,因此此处的Token是用来实例代币合约的工厂。

const Token = await ethers.getContractFactory("Token");

6.在ContractFactory上调用deploy()将启动部署,并返回解析为Contract的Promise。 该对象包含了智能合约所有函数的方法。

const hardhatToken = await Token.deploy();

7.当调用deploy()时,将发送交易,但是直到该交易打包出块后,合约才真正部署。 调用deployed()将返回一个Promise,因此该代码将阻塞直到部署完成。

await hardhatToken.deployed();

8.部署合约后,我们可以在hardhatToken 上调用合约方法,通过调用balanceOf()来获取所有者帐户的余额。请记住,部署合约的帐户获得了全部代币,在使用 hardhat-ethers 插件时,默认情况下, ContractFactory和Contract实例连接到第一个签名者。 这意味着owner变量中的帐户执行了部署,而balanceOf()应该返回全部发行量。

const ownerBalance = await hardhatToken.balanceOf(owner.getAddress());

9.在这里,再次使用Contract实例调用Solidity代码中合约函数。 totalSupply()返回代币的发行量,我们检查它是否等于ownerBalance。

expect(await hardhatToken.totalSupply()).to.equal(ownerBalance);

5.完整覆盖测试

// We import Chai to use its asserting functions here.

const { expect } = require("chai");

  


// `describe` is a Mocha function that allows you to organize your tests. It's

// not actually needed, but having your tests organized makes debugging them

// easier. All Mocha functions are available in the global scope.

  


// `describe` receives the name of a section of your test suite, and a callback.

// The callback must define the tests of that section. This callback can't be

// an async function.

describe("Token contract", function () {

// Mocha has four functions that let you hook into the the test runner's

// lifecyle. These are: `before`, `beforeEach`, `after`, `afterEach`.

  


// They're very useful to setup the environment for tests, and to clean it

// up after they run.

  


// A common pattern is to declare some variables, and assign them in the

// `before` and `beforeEach` callbacks.

  


let Token;

let hardhatToken;

let owner;

let addr1;

let addr2;

let addrs;

  


// `beforeEach` will run before each test, re-deploying the contract every

// time. It receives a callback, which can be async.

beforeEach(async function () {

// Get the ContractFactory and Signers here.

Token = await ethers.getContractFactory("Token");

[owner, addr1, addr2, ...addrs] = await ethers.getSigners();

  


// To deploy our contract, we just have to call Token.deploy() and await

// for it to be deployed(), which happens onces its transaction has been

// mined.

hardhatToken = await Token.deploy();

await hardhatToken.deployed();

  


// We can interact with the contract by calling `hardhatToken.method()`

await hardhatToken.deployed();

});

  


// You can nest describe calls to create subsections.

describe("Deployment", function () {

// `it` is another Mocha function. This is the one you use to define your

// tests. It receives the test name, and a callback function.

  


// If the callback function is async, Mocha will `await` it.

it("Should set the right owner", async function () {

// Expect receives a value, and wraps it in an assertion objet. These

// objects have a lot of utility methods to assert values.

  


// This test expects the owner variable stored in the contract to be equal

// to our Signer's owner.

expect(await hardhatToken.owner()).to.equal(await owner.getAddress());

});

  


it("Should assign the total supply of tokens to the owner", async function () {

const ownerBalance = await hardhatToken.balanceOf(owner.getAddress());

expect(await hardhatToken.totalSupply()).to.equal(ownerBalance);

});

});

  


describe("Transactions", function () {

it("Should transfer tokens between accounts", async function () {

// Transfer 50 tokens from owner to addr1

await hardhatToken.transfer(await addr1.getAddress(), 50);

const addr1Balance = await hardhatToken.balanceOf(

await addr1.getAddress()

);

expect(addr1Balance).to.equal(50);

  


// Transfer 50 tokens from addr1 to addr2

// We use .connect(signer) to send a transaction from another account

await hardhatToken.connect(addr1).transfer(await addr2.getAddress(), 50);

const addr2Balance = await hardhatToken.balanceOf(

await addr2.getAddress()

);

expect(addr2Balance).to.equal(50);

});

  


it("Should fail if sender doesn’t have enough tokens", async function () {

const initialOwnerBalance = await hardhatToken.balanceOf(

await owner.getAddress()

);

  


// Try to send 1 token from addr1 (0 tokens) to owner (1000 tokens).

// `require` will evaluate false and revert the transaction.

await expect(

hardhatToken.connect(addr1).transfer(await owner.getAddress(), 1)

).to.be.revertedWith("Not enough tokens");

  


// Owner balance shouldn't have changed.

expect(await hardhatToken.balanceOf(await owner.getAddress())).to.equal(

initialOwnerBalance

);

});

  


it("Should update balances after transfers", async function () {

const initialOwnerBalance = await hardhatToken.balanceOf(

await owner.getAddress()

);

  


// Transfer 100 tokens from owner to addr1.

await hardhatToken.transfer(await addr1.getAddress(), 100);

  


// Transfer another 50 tokens from owner to addr2.

await hardhatToken.transfer(await addr2.getAddress(), 50);

  


// Check balances.

const finalOwnerBalance = await hardhatToken.balanceOf(

await owner.getAddress()

);

expect(finalOwnerBalance).to.equal(initialOwnerBalance - 150);

  


const addr1Balance = await hardhatToken.balanceOf(

await addr1.getAddress()

);

expect(addr1Balance).to.equal(100);

  


const addr2Balance = await hardhatToken.balanceOf(

await addr2.getAddress()

);

expect(addr2Balance).to.equal(50);

});

});

![Image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b754f5a77f024f61a874b395766298a4~tplv-k3u1fbpfcp-watermark.image?)
});

6.使用npx hardhat test命令行运行:

Image.png