以太坊Dapp 编译脚本 + 部署脚本 + 测试脚本

目录

目录结构

创建包管理

安装依赖

编译脚本

安装依赖

准备合约源码

准备编译脚本

执行编译脚本

部署脚本 

安装依赖

准备部署脚本

执行部署脚本 

测试脚本

安装依赖

准备测试脚本

执行测试脚本

测试例子

 测试脚本

执行测试脚本 

将以上工作流步骤串起来

修改 package.json  

 执行命令


目录结构

手动创建 contract_workflow 文件夹

并在其目录下创建 compiled、contracts、scripts、test 四个文件夹

  • contracts 存放源代码
  • scripts 存放编译脚本
  • compiled 存放编译结果
  • test 存放测试文件

创建包管理

在 contract_workflow 目录下,执行以下命令

会生成 package.json 文件

npm init

安装依赖

在 contract_workflow 路径下,执行以下命令

安装ganache-cli,附加生成 node_modules 和 package-lock.json 文件

npm install [email protected] --save-dev

以太坊Dapp 编译脚本 + 部署脚本 + 测试脚本_第1张图片

ganache-cli 仅仅用作测试用,每一次重新运行都会重新生成10个新的账号

运行 ganache-cli 后面用作测试用

./node_modules/.bin/ganache-cli

编译脚本

安装依赖

在 contract_workflow 路径下,执行以下命令

npm install [email protected]

准备合约源码

contracts 目录下 新建合约 Voting.sol

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.4.25 <0.9.0;

contract Voting{
    bytes32[] public candidateList;
    mapping(bytes32 => uint8) public votesReceived;
    constructor(bytes32[] memory candidateListName) public{
        candidateList = candidateListName;
    }

    function validateCandidate(bytes32 candidateName) internal view returns(bool){
        for (uint8 i = 0; i < candidateList.length; i++){
            if(candidateName == candidateList[i]){
                return true;
            }
        }
        return false;
    }

    function vote(bytes32 candidateName) public{
        require(validateCandidate(candidateName));
        votesReceived[candidateName] += 1;
    }

    function totalVotesFor(bytes32 candidateName) public view returns(uint8){
        require(validateCandidate(candidateName));
        return votesReceived[candidateName];
    }
}

准备编译脚本

scripts 目录下 新建脚本 compile.js

const fs = require('fs-extra');
const solc = require('solc');
const path = require('path');

// resolve 将后面的路径拼接起来
// __dirname 表示当前目录, 两个下划线_

// 清除编译结果, 保留最新编译结果, 保障一致性
const compiledDir = path.resolve(__dirname, '../compiled');
fs.removeSync(compiledDir);
fs.ensureDirSync(compiledDir);

const contractDir = path.resolve(__dirname, '../contracts', 'Voting.sol')
// 同步读取文件
const contractSource = fs.readFileSync(contractDir,'utf-8');
let compiledResult = solc.compile(contractSource, 1);

console.log(compiledResult);

// solidity合约, 如果报错, 错误定位
if(Array.isArray(compiledResult.errors) && compiledResult.errors.length){
	throw new Error(compiledResult.errors[0])
}

// 导出编译后结果 compiledResult, 也可以使用 module.export
Object.keys(compiledResult.contracts).forEach(name=>{
	// 正则表达式取:开头之后的字符串
	let contractName = name.replace(/^:/,'');
	let filePath = path.resolve(__dirname, '../compiled', `${contractName}.json`);
	// 输出
	fs.outputJsonSync(filePath, compiledResult.contracts[name]);
	console.log("Saving json file to ",filePath);
})

执行编译脚本

在 contract_workflow 路径下,执行以下命令

node ./scripts/compile.js

以太坊Dapp 编译脚本 + 部署脚本 + 测试脚本_第2张图片


部署脚本 

安装依赖

在 contract_workflow 路径下,执行以下命令

npm install [email protected]

准备部署脚本

scripts 目录下 新建脚本 deploy.js

const Web3 = require('web3');
let web3;
if (typeof web3 !== 'undefined') {
	//当你之前如果使用过MetaMask钱包等时,你的provider可能已经设置好了,web3.currentProvider就是你目前的provider
	web3 = new Web3(web3.currentProvider);
} else {
	// set the provider you want from Web3.providers
	web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
}


const fs = require('fs-extra');
const path = require('path');

const contractDir = path.resolve(__dirname, '../compiled', 'Voting.json');
const {interface, bytecode} = require(contractDir);
let arguments = [[web3.utils.fromAscii('Alice'),web3.utils.fromAscii('Bob'),web3.utils.fromAscii('Cary')]]
let gas = 5000000;

(async () => {
	let accounts = await web3.eth.getAccounts();
	console.time("deploy time");
	let result = await new web3.eth.Contract(JSON.parse(interface))
				.deploy({
					data:bytecode,
					arguments:arguments
				})
				.send({
					from:accounts[0],
					gas:gas
				})
	console.timeEnd("deploy time")
	
	// 输出合约地址
	console.log("contract address: ",result.options.address);
})();

执行部署脚本 

在 contract_workflow 路径下,执行以下命令

执行前确定你的私链正在运行

node ./scripts/deploy.js

 


测试脚本

mocha 是一个 JavaScript 的测试框架

既可以在浏览器环境中运行,也可以在 node.js 环境下运行

mocha 主要特点有:

  • 既可以测试简单的 JavaScript 函数,又可以测试异步代码
  • 可以自动运行所有测试,也可以只运行特定的测试
  • 可以支持 before、after、beforeEach 和 afterEach 来编写初始化代码

安装依赖

npm install [email protected] --save-dev

准备测试脚本

在 test 目录下,新建脚本 VotingTest.js

const assert = require('assert');
const path = require('path');
const ganache = require('ganache-cli');
const Web3 = require('web3');

const web3 = new Web3(ganache.provider());

const contractDir = path.resolve(__dirname, '../compiled', 'Voting.json');
const {interface, bytecode} = require(contractDir);
let contract;
let accounts;
let arguments = [[web3.utils.fromAscii('Alice'),web3.utils.fromAscii('Bob'),web3.utils.fromAscii('Cary')]]
let gas = 5000000;



describe('#contract',()=>{
	before(()=>{
		console.log('test start')
	})
	
	after(()=>{
		console.log('test end')
	})
	
	afterEach(()=>{
		console.log('current test completed')
	})
	
	// beforeEach:使每一次测试的环境都是干净的, 每次合约执行前都执行
	beforeEach(async()=>{
		accounts = await web3.eth.getAccounts();
		contract = await new web3.eth.Contract(JSON.parse(interface))
					.deploy({
						data:bytecode,
						arguments:arguments
					})
					.send({
						from:accounts[0],
						gas:gas
					})
		console.log('current test start')
	});
	
	it('deployed contract successfully',()=>{
		assert.ok(contract.options.address);
	})
	it('should be able to check the number of votes',async()=>{
		let aVoter = web3.utils.fromAscii('Alice');
		assert.ok(contract.methods.totalVotesFor(aVoter).call({from:accounts[0]}));
	})
	it('should be able to vote',async()=>{
		let voteTo = web3.utils.fromAscii('Bob')
		let BeforeNumOfVote = parseInt(await contract.methods.totalVotesFor(voteTo).call({from:accounts[0]}));
		await contract.methods.vote(voteTo).send({from:accounts[0]});
		let AfterNumOfVote = parseInt(await contract.methods.totalVotesFor(voteTo).call({from:accounts[0]}));
		assert.equal(BeforeNumOfVote + 1,AfterNumOfVote);
	})
})

执行测试脚本

在 test 目录下执行,以下命令

../node_modules/mocha/bin/mocha VotingTest.js

以太坊Dapp 编译脚本 + 部署脚本 + 测试脚本_第3张图片

测试例子

 测试脚本

在 test 下创建 sum.js 和 testSum.js

执行测试脚本 

在 contract_workflow 目录下,执行以下命令 

mocha 会自动寻找目录下叫 test 的文件夹,并执行其中 所有的 js 文件

./node_modules/mocha/bin/mocha

以太坊Dapp 编译脚本 + 部署脚本 + 测试脚本_第4张图片

在 test 目录下,执行以下命令

测试特定的  js 文件 或 文件夹

../node_modules/mocha/bin/mocha testSum.js

以太坊Dapp 编译脚本 + 部署脚本 + 测试脚本_第5张图片


将以上工作流步骤串起来

 参考链接:

npm scripts 使用指南 - 阮一峰的网络日志https://www.ruanyifeng.com/blog/2016/10/npm_scripts.html

修改 package.json  

我们可以通过 npm script 机制,将这些智能合约的工作流串起来

修改 package.json 如下图

 ./node_modules/mocha/bin/ 可以省略 ,因为npm scripts 在执行是将./node_modules/中的包暂时加入了环境变量中

  "scripts": {
    "compile": "node scripts/compile.js",
    "predeploy": "npm run compile",
    "deploy": "node scripts/deploy.js",
    "pretest": "npm run compile",
    "test": "mocha test/"
  },

以太坊Dapp 编译脚本 + 部署脚本 + 测试脚本_第6张图片

 执行命令

npm run test

以太坊Dapp 编译脚本 + 部署脚本 + 测试脚本_第7张图片

你可能感兴趣的:(Dapp,区块链)