https://mochajs.org/ 學習網址: https://www.jianshu.com/p/9c78548caffa https://www.jb51.net/article/106463.htm 在truffle框架的簡單使用中,我們了解到它的測試模塊是包裝了mocha測試框架的,在這里我們選擇cryptopunks的truffle例子來相應講解:
https://github.com/larvalabs/cryptopunks
為什么要使用mocha這個測試模塊:
當我們在開發,我們往往會有以下的問題:
參考:https://blog.csdn.net/imwebteam/article/details/53310958
需求和開發脫節
當一份需求來了, 開發人員往往不能百分百的理解需求的內容(拋棄產品自己變更需求的可能性。。),這往往會讓開發人員開發出的功能會有跟需求有所差別,這會帶來額外的工作量
開發和測試脫節
什么是開發和測試脫節,說的是,當開發人員按照自己的想法開發完了一個需求。然后測試人員也按照自己的想法去測試這個需求,然后由於雙方的分歧,導致測試認為開發有bug,開發認為測試是sb.
那么如何解決上面的問題呢?
答案就是 選擇一種軟件敏捷開發模式
敏捷開發模式
目前比較流行的開發模式有兩種: TDD 和 BDD
TDD (Test Driven Development 測試驅動開發)
- 測試來驅動開發
- 其重點偏向開發
- 測試用例是在約束開發者,使開發者的目標明確,設計出滿足需求的系統
BDD (Behaviour Driven Development 行為驅動開發)
- 基於TDD發展,保持測試先行的理念
- 其重點偏向設計
- 在測試代碼中用一種自然通用語言的方式把系統的行為描述出來
- 將系統的設計和測試用例結合起來,進而驅動開發工作
兩種方式各有其特點,我們通常選擇的是BDD的方式
為了方便我們編寫測試用例,我們需要使用一些前端測試用例工具——mocha
在這個例子中我們能夠看見在其的test文件夾中寫了一些測試文件,比如我們以cryptopunksmarket-setinitia.jsl這個文件為例,看cryptopunks測試代碼cryptopunksmarket-setinitial.js
(1)Truffle測試框架學習:
參考:http://www.blockchainbrother.com/article/2082
Truffle 使用 Mocha 測試框架 和 Chai 斷言來給你提供一個可靠的框架編寫JavaScript測試。這里需要注意到的一個很大的不同是它使用了contract()測試套件替代了describe()測試套件,這個函數基本和 describe() 一樣,只不過它可以啟用clean-room 功能. 其過程如下:
(2)后面想要使用測試框架mocha,但是又不想使用truffle,所以就來學習這個框架怎么單獨使用了,下面就是學習的過程:
當然,首先要安裝:
使用npm全局安裝:
npm install --g mocha
或者僅僅只是安裝在某個模塊:
npm install --save mocha
然后你就可以使用了
1.要測試上面的代碼是否對的,因此就要編寫測試腳本,測試腳本與所要測試的源碼腳本同名,但是后綴名為 .test.js或 .spec.js, 如:xx.test.js 或 xx.spec.js
2.測試腳本可以包含一個或多個describe塊,describe塊稱為 "測試套件",表示一組相關的測試,它是一個函數,有兩個參數,第一個參數是測試套件的名稱,第二個參數是一個實際執行的函數。
每個describe塊也可以包含一個或多個it塊,it塊稱為 “測試用例",表示一個單獨的測試,是測試的最小單位,它也是一個函數,第一個參數也是測試用例的名稱,第二個參數是一個實際執行的函數,it塊之間是同步運行的。
describe 和 it 大量嵌套后,就形成了一顆樹。樹的非葉子節點都是測試集合,葉子節點即 it ,就是測試用例。
注意,如果一個describe 里面沒有 it (比如下面:), Mocha將不會執行這個 describe。
3.理解斷言庫
學習文檔:http://www.chaijs.com
斷言庫可以理解為比較函數,也就是斷言函數是否和預期一致,如果一致則表示測試通過,如果不一致表示測試失敗。mocha本身是不包括斷言庫的,所以必須引入第三方斷言庫的,目前比較受歡迎的斷言庫有 should.js, expect.js, chai.
should.js是BDD風格
expect.js是expect風格的斷言
//下面介紹的是chai
chai的expect(), assert() 和 should的斷言
Mocha默認使用的是BDD的風格。expect和should都是BDD的風格,二者使用相同的鏈式語言來組織斷言的,但不同在於他們初始化斷言的方式,expect使用
構造函數來創建斷言對象實例,而should通過為 Object.prototype新增方法來實現斷言(should不支持IE),expect直接指向 chai.expect,
should則是 chai.should();
上面的代碼中 expect 是斷言的意思,該作用是判斷源碼的實際執行結果與預期結果是否一致,如果不一致就拋出一個錯誤
三種的使用方法簡單如下所示:
var expect = require('chai').expect;
expect(2).to.be.equal(2);
var assert = require('chai').
assert;
assert.equal((await contract.totalSupply()).toNumber(), 10);
var should = require('chai').should();
cookies.should.not.be.empty;
cookies.id.should.equal('10001','not equal to 10001');
因此在執行上面代碼之前,
我們需要在項目中安裝 chai, 如下命令:
npm install --save-dev chai
1)var expect = require('chai').expect;
即引用 chai 斷言庫,使用的是 expect斷言風格。
expect 官網API(http://chaijs.com/api/bdd/).
2)mocha測試代碼如何運行?
上面的add.test.js 編寫完成后,我們需要運行測試代碼了,進入add.test.js代碼的目錄后,執行如下命令可運行:
mocha add.test.js
mocha命令后面也可以指定多個文件,如下命令:
mocha xx.test.js yy.test.js
使用通配符:
或者我們可以運行如下命令,執行多個測試腳本文件:
mocha spec/{add,reduce}.js //目錄spec下的add.js和reduce.js文件
mocha spec/*.js //所有文件
mocha默認運行test子目錄里面的測試腳本,我們一般情況下,可以把測試腳本放在test目錄下,然后進入test的上層目錄,直接執行mocha命令即可:
mocha
然后你就會發現test目錄下第一層的所以測試文件都被運行了。命令只會執行test第一層目錄下所有文件,並不能執行嵌套目錄下的文件。
為了執行所有嵌套目錄下的文件,我們可以 mocha命令后面加一個參數 --recursive 參數
更多的細節信息可以看:https://www.cnblogs.com/tugenhua0707/p/8419534.html,當然之后你要補充一下
3)測試用例的鈎子
Mocha在describe塊之中,提供了測試用例的四個鈎子,before(), after(), beforeEach()和afterEach(),他們會在指定的時間內執行。
before(): 將會在所有測試用例執行之前運行,比如在之前插入數據等等操作。
after(): 會在所有測試執行之后運行,用於清理測試環境,回滾到清空數據狀態。
beforeEach(): 將會在每個測試用例執行之前執行,可用於測試測試需要准備相關數據的條件。
afterEach(): 將會在每個測試用例之后執行,可用於准備測試用例所需的后置條件。
4)理解異步鈎子函數
例子1:
var expect = require('chai').expect;
describe('異步鈎子函數', function() {
var foo = false;
beforeEach(function(){
setTimeout(function(){
foo = true;
}, 50)
});
it('異步鈎子函數成功', function() {
expect(foo).to.be.equal(true);
})
});
結果:
異步鈎子函數
異步鈎子函數成功:
AssertionError: expected false to equal true
+ expected - actual
-false
+true
如上可以看到測試失敗,原因是因為setTimeout 是異步的,在setTimeout執行完之前,it函數已經被執行了,所以foo當時數據還是false,
因此false不等於true了。
這時候 done參數出來了,在回調函數存在時候,它會告訴mocha,你正在編寫一個異步測試,會等到異步測試完成的時候來調用done函數,或者超過2秒后超時,如下代碼就可以成功了;
var expect = require('chai').expect;
describe('異步鈎子函數', function() {
var foo = false;
beforeEach(function(done){
setTimeout(function(){
foo = true;
// complete the async beforeEach
done();
}, 50)
});
it('異步鈎子函數成功', function() {
expect(foo).to.be.equal(true);
});
});
(3)這篇文章不是要很詳細地告訴大家概念性的內容,只是讓有跟我一樣想法(即學習了truffle框架后,發現里面的那個測試十分有意思,想之后做測試的時候也用這種測試方法),但是突然不知道怎么入手的人一個方向,知道那是個什么測試框架,以及去哪里學習,以及一些比較簡單的必須知道的概念和內容,知道這些其實你就可以寫一個十分簡單的測試例子了,建議結合別人的測試代碼進行學習,如https://github.com/larvalabs/cryptopunks/tree/master/test
在下面進行測試:
1)一開始,當我想要直接復制truffle中寫好的測試文件,進行小部分更改直接進行使用時,發現出錯:
truffle中只需要這兩句話就可以部署好合約,但是在沒框架的情況下是不可以的
var testToken = artifacts.require(“./test-punk.sol”);
instance = await testToken.deployed(50);
這樣會報錯:
1.ReferenceError: artifacts is not defined
2.(function (exports, require, module, __filename, __dirname) { pragma solidity ^0.4.20;
^^^^^^^^
SyntaxError: Unexpected identifier
所以這就說明了在框架外是不可以這樣子進行合約的編譯的
通過上面我們就能夠知道truffle到使用上面兩句部署指令前,還進行了編譯compile和部署migrate,所以在測試前要將合約的編譯和部署都弄好,你可以通過查看我寫的remix的使用來學怎么使用remix進行編譯和部署或者是看nodejs部署智能合約的方法來自己編寫代碼進行編譯和部署,最終得到合約的部署地址NFMAddress
部署成功后,之后如果想在別的地方進行使用,需要以下幾句話句話:
const NFMAbi = require("./testToken.json");//合約生成的Abi,一般為json文件
const NFMContract = web3.eth.contract(NFMAbi);
const instance = NFMContract.at(NFMAddress);
此時就能夠調用該函數中的函數及變量了
2)其次,還要記得將相應的模塊包下載下來,這里要添加package.json文件配置等內容
Error: Cannot find module ‘web3'
你安裝的web3一定要放在本地的node_modules文件夾下,不然是讀不出來的
3)使用nodejs來進行合約的編譯和部署時,發現出現下面的錯誤
1.let abi = compiledContract.contracts['testToken'].interface;
出錯:
TypeError: Cannot read property 'interface' of undefined
然后輸出compiledContract進行查看
2.console.log(compiledContract);
發現在編譯處就出錯了
{ contracts: {},
errors:
[ ':49:17: ParserError: Expected identifier, got \'LParen\'\n constructor (uint number) public{\n ^\n' ],
sourceList: [ '' ],
sources: {} }
還有:
{ contracts: {},
errors:
[ ':26:9: TypeError: Wrong argument count for function call: 2 arguments given but expected 1.\n require(propertyValueToOwner[propertyValue] == 0x0,\'this is not the first-sell\');\n ^------------------------------------------------------------------------------^\n',
發現可能是版本的問題,因為這里聲明構造函數使用了新的聲明方式,但是在這里沒能被識別出。所以下載了新版本的solc,然后就成功了
4)
contract('testToken',async (accounts) => {
出錯:ReferenceError: contract is not defined
因為contract是truffle框架弄的,不在框架中是不能夠這樣使用的,這時候想要使用賬號只能老實地連接區塊鏈,通過web3模塊去調用API接口
var Web3 = require("web3");
web3.setProvider(new Web3.providers.HttpProvider("http://localhost:8201"));
account1 = web3.eth.accounts[0];
5)最后運行結果也有問題:
用戶deMBP:testToken 用戶$ mocha test-mocha.js
testToken Test
check tokenNumber
1) deploy contract
2) sell token
3) buy token
4) check the balance
5) withdrawl the balance
0 passing (118ms)
5 failing
1) testToken Test
deploy contract:
TypeError: Cannot read property 'call' of undefined
at Context.<anonymous> (test-mocha.js:56:43)
2) testToken Test
sell token:
TypeError: Cannot read property 'sellToken' of undefined
at Context.<anonymous> (test-mocha.js:77:19)
3) testToken Test
buy token:
TypeError: Cannot read property 'testTokenIdToOwner' of undefined
at Context.<anonymous> (test-mocha.js:90:25)
4) testToken Test
check the balance:
TypeError: Cannot read property 'pendingDrawalOfUser' of undefined
at Context.<anonymous> (test-mocha.js:112:38)
5) testToken Test
withdrawl the balance:
TypeError: Cannot read property 'pendingDrawalOfUser' of undefined
at Context.<anonymous> (test-mocha.js:124:31)
Contract mined! address: 0x3cb4464f73eda60ac3ba1d46cd0544cd7ae18040 transactionHash: 0x62f27e3ccdfcb6ba54547107bbc8f1f9f152f0f588eddec9b1dc3a65d1d7d047
后面發現這個原因是部署回調得到instance前,it函數中的內容就已經開始調用了,這樣的話怎么着instance都是undefined的,那么肯定是不可能能夠調用合約中的函數的。而且在這里將部署函數放在了一個單獨的it測試用例當中,但是下面的測試用例都是應該等待部署完成后才能夠測試成功的,那么這樣就不可能成功測試了,因為it測試用例是同步進行的,在部署的同時,其他測試用例也都開始運行了,⚠️有前后關系的測試內容應該要寫在一個測試用例當中。
所以就不做在這之前進行部署的事情了,畢竟我們肯定在測試之前是已經把合約部署上去的了,那么只要用
let instance = MyContract.at('0x86757c9bdea10815e7d75a1577b6d9d2825dae0a');
這句話就行了
6)然后再運行也出錯:
mocha Error: Timeout of 2000ms exceeded.
則需要你再運行的時候添加
mocha -t 20000 test.js
因為其默認的時間是2000ms,你的時間如果過大,可以進行自己設置
7)當你使用describe這些mocha的形式語句的時候,使用node test.js是不能運行成功的,會返回錯誤:
用戶MBP:testToken 用戶$ node test.js
/Users/用戶/testToken/test.js:165
describe('testToken',function(){
^
ReferenceError: describe is not defined
所以一定要用mocha開頭,mocha test.js
實現:
const debug = require("debug")("testToken"); const assert = require('assert') // var testToken = require("./test-punk.sol"); const Web3 = require('web3'); const web3 = new Web3(); web3.setProvider(new Web3.providers.HttpProvider('http://127.0.0.1:7545')); const fs = require("fs"); const solc = require("solc"); let source = fs.readFileSync("testToken.sol",'utf8');//read file let compiledContract = solc.compile(source,1);//compile // console.log(compiledContract); for (let contractName in compiledContract.contracts) { console.log("in"); var bytecode = compiledContract.contracts[contractName].bytecode; var abi = JSON.parse(compiledContract.contracts[contractName].interface); //將abi寫成json形式 console.log("out"); } // 你要測試的是接口 // console.log(bytecode); // console.log(abi); let gasEstimate = web3.eth.estimateGas({data:'0x'+bytecode}); let MyContract = web3.eth.contract(abi); debug("deploying contract"); // let instance; let instance = MyContract.at('0x86757c9bdea10815e7d75a1577b6d9d2825dae0a');//可改 var user1 = web3.eth.accounts[0]; console.log(user1); var user2 = web3.eth.accounts[1]; var user3 = web3.eth.accounts[2]; var user4 = web3.eth.accounts[3]; var user5 = web3.eth.accounts[4]; var propertyValues = ['0x00000001','0x00000002','0x00000003','0x00000004','0x00000005']; var prices = [11,12,13,14,15]; var users = [user1,user2,user3,user4,user5]; //這個地方是想要在這里同時部署合約,但是發現總是不成功,部署異步總是太慢,before()函數好像也沒有什么用,所以后面我也只能放棄部署了,只能先部署完得到地址再來調用了 // function deployContract() { // instance = MyContract.new(50,{from:user1,data:'0x'+bytecode,gas:47000000},function(e,contract){ // if(typeof contract.address !== 'undefined'){ // console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash); // } // }); // } // before(deployContract); // var instance; describe("testToken Test",function(){ // it("deploy contract",async function(){ it("contract begin",async function(){ let num = propertyValues.length; // instance = await testToken.deployed(50); console.log(instance.address); console.log('check tokenNumber'); console.log(await instance.tokenNumber.call()); debug("create token");
//首先先創建5個token for(let i=0; i<num; i++){ await instance.create(propertyValues[i],prices[i],{from:users[i],value:prices[i],gas:30000000}); } debug("display token");
//然后查看生成的token的屬性 let nextTokenId = await instance.nextTokenToCreate.call(); console.log("nextTokenId is :"+ nextTokenId); if(parseInt(nextTokenId) != 5){ console.log("initial token failed"); }else{ for(let i =0; i<num; i++){ console.log(await instance.propertyOfToken.call(i)); } } }); //然后sell token it("sell token",async function(){ await instance.sellToken(0,21,{from:user1,gas:30000000}); await instance.sellToken(1,22,{from:user2,gas:30000000}); //查看是否成功sell console.log(await instance.tokenIdToSell.call(0)); console.log(await instance.tokenIdToSell.call(1)); }); //其他用戶對sell的token進行購買 it("buy token",async function(){
//查看購買前token的擁有者是誰 console.log(instance.testTokenIdToOwner.call(0)); console.log(instance.testTokenIdToOwner.call(1)); await instance.buyToken(0,{from:user3,value:21,gas:30000000}); await instance.buyToken(1,{from:user4,value:22,gas:30000000}); let buyer1 = await instance.testTokenIdToOwner.call(0); let buyer2 = await instance.testTokenIdToOwner.call(1); //查看購買后的擁有者是誰來核實購買成功進行 console.log(buyer1); console.log(buyer2); console.log("check the sell after buying"); console.log(await instance.tokenIdToSell.call(0)); console.log(await instance.tokenIdToSell.call(1)); assert.equal(buyer1,user3,"buying 0 is failed"); assert.equal(buyer2,user4,"buying 1 is failed"); }); //查看用戶臨時賬戶中此時有多少積蓄 it("check the balance",async function(){ let user1Balance = await instance.pendingDrawalOfUser.call(user1); let user2Balance = await instance.pendingDrawalOfUser.call(user2); console.log(user1Balance); console.log(user2Balance); assert.equal(user1Balance,21,"user1 balance number is not right"); assert.equal(user2Balance,22,"user1 balance number is not right"); }); //然后將臨時賬戶中的錢轉到錢包中 it("withdrawl the balance",async function(){ console.log(await instance.pendingDrawalOfUser.call(user1)); console.log(await instance.pendingDrawalOfUser.call(user2)); await instance.withdrawl({from:user1,gas:30000000}); await instance.withdrawl({from:user2,gas:30000000}); console.log(await instance.pendingDrawalOfUser.call(user1)); console.log(await instance.pendingDrawalOfUser.call(user2)); }); }); 結果是: 用戶deMBP:testToken 用戶$ mocha test-mocha.js in out 0x3455f15cc11f2e77c055f931a6c918ccc7c18fd8 testToken Test 0x86757c9bdea10815e7d75a1577b6d9d2825dae0a check tokenNumber BigNumber { s: 1, e: 1, c: [ 50 ] } nextTokenId is :5
//生成的token的屬性 [ BigNumber { s: 1, e: 0, c: [ 0 ] }, '0x0000000100000000000000000000000000000000000000000000000000000000', '0x3455f15cc11f2e77c055f931a6c918ccc7c18fd8', BigNumber { s: 1, e: 1, c: [ 11 ] } ] [ BigNumber { s: 1, e: 0, c: [ 1 ] }, '0x0000000200000000000000000000000000000000000000000000000000000000', '0x7ddad6a67544efb0c51808c77009a7b98cc81630', BigNumber { s: 1, e: 1, c: [ 12 ] } ] [ BigNumber { s: 1, e: 0, c: [ 2 ] }, '0x0000000300000000000000000000000000000000000000000000000000000000', '0xe9478ebcf4c755ad945a351261c8fa046672963b', BigNumber { s: 1, e: 1, c: [ 13 ] } ] [ BigNumber { s: 1, e: 0, c: [ 3 ] }, '0x0000000400000000000000000000000000000000000000000000000000000000', '0x920f422b761976972a9eadbec1f5341a9747ea6a', BigNumber { s: 1, e: 1, c: [ 14 ] } ] [ BigNumber { s: 1, e: 0, c: [ 4 ] }, '0x0000000500000000000000000000000000000000000000000000000000000000', '0xa17a7fa74a7dd57dff005b45234292e7daaf150c', BigNumber { s: 1, e: 1, c: [ 15 ] } ] ✓ contract begin (1541ms)
// [ true, '0x0000000100000000000000000000000000000000000000000000000000000000', BigNumber { s: 1, e: 0, c: [ 0 ] }, '0x3455f15cc11f2e77c055f931a6c918ccc7c18fd8', BigNumber { s: 1, e: 1, c: [ 21 ] } ] [ true, '0x0000000200000000000000000000000000000000000000000000000000000000', BigNumber { s: 1, e: 0, c: [ 1 ] }, '0x7ddad6a67544efb0c51808c77009a7b98cc81630', BigNumber { s: 1, e: 1, c: [ 22 ] } ] ✓ sell token (663ms) 0x3455f15cc11f2e77c055f931a6c918ccc7c18fd8 0x7ddad6a67544efb0c51808c77009a7b98cc81630 0xe9478ebcf4c755ad945a351261c8fa046672963b 0x920f422b761976972a9eadbec1f5341a9747ea6a check the sell after buying [ false, '0x0000000100000000000000000000000000000000000000000000000000000000', BigNumber { s: 1, e: 0, c: [ 0 ] }, '0x0000000000000000000000000000000000000000', BigNumber { s: 1, e: 0, c: [ 0 ] } ] [ false, '0x0000000200000000000000000000000000000000000000000000000000000000', BigNumber { s: 1, e: 0, c: [ 1 ] }, '0x0000000000000000000000000000000000000000', BigNumber { s: 1, e: 0, c: [ 0 ] } ] ✓ buy token (954ms) BigNumber { s: 1, e: 1, c: [ 21 ] } BigNumber { s: 1, e: 1, c: [ 22 ] } ✓ check the balance (206ms) BigNumber { s: 1, e: 1, c: [ 21 ] } BigNumber { s: 1, e: 1, c: [ 22 ] } BigNumber { s: 1, e: 0, c: [ 0 ] } BigNumber { s: 1, e: 0, c: [ 0 ] } ✓ withdrawl the balance (631ms) 5 passing (4s)
注意:在這里看好像這個例子也能夠順序執行,但是后面發現上面例子的所以it應該合成一個it來寫,不然順序是不一定能保證的,因為it測試用例在運行時是同步運行的,但是我這里的測試用例其實是有希望它按照順序來運行,所以改為:
describe("testToken Test",function(){ it("contract begin",async function(){ let num = propertyValues.length; // instance = await testToken.deployed(50); console.log(instance.address); console.log('check tokenNumber'); console.log(await instance.tokenNumber.call()); debug("create token"); //首先先創建5個token for(let i=0; i<num; i++){ await instance.create(propertyValues[i],prices[i],{from:users[i],value:prices[i],gas:30000000}); } debug("display token"); //然后查看生成的token的屬性 let nextTokenId = await instance.nextTokenToCreate.call(); console.log("nextTokenId is :"+ nextTokenId); if(parseInt(nextTokenId) != 5){ console.log("initial token failed"); }else{ for(let i =0; i<num; i++){ console.log(await instance.propertyOfToken.call(i)); } } //然后sell token await instance.sellToken(0,21,{from:user1,gas:30000000}); await instance.sellToken(1,22,{from:user2,gas:30000000}); //查看是否成功sell console.log(await instance.tokenIdToSell.call(0)); console.log(await instance.tokenIdToSell.call(1)); //其他用戶對sell的token進行購買 //查看購買前token的擁有者是誰 console.log(instance.testTokenIdToOwner.call(0)); console.log(instance.testTokenIdToOwner.call(1)); await instance.buyToken(0,{from:user3,value:21,gas:30000000}); await instance.buyToken(1,{from:user4,value:22,gas:30000000}); let buyer1 = await instance.testTokenIdToOwner.call(0); let buyer2 = await instance.testTokenIdToOwner.call(1); //查看購買后的擁有者是誰來核實購買成功進行 console.log(buyer1); console.log(buyer2); console.log("check the sell after buying"); console.log(await instance.tokenIdToSell.call(0)); console.log(await instance.tokenIdToSell.call(1)); assert.equal(buyer1,user3,"buying 0 is failed"); assert.equal(buyer2,user4,"buying 1 is failed"); //查看用戶臨時賬戶中此時有多少積蓄 let user1Balance = await instance.pendingDrawalOfUser.call(user1); let user2Balance = await instance.pendingDrawalOfUser.call(user2); console.log(user1Balance); console.log(user2Balance); assert.equal(user1Balance,21,"user1 balance number is not right"); assert.equal(user2Balance,22,"user1 balance number is not right"); //然后將臨時賬戶中的錢轉到錢包中 console.log(await instance.pendingDrawalOfUser.call(user1)); console.log(await instance.pendingDrawalOfUser.call(user2)); await instance.withdrawl({from:user1,gas:30000000}); await instance.withdrawl({from:user2,gas:30000000}); console.log(await instance.pendingDrawalOfUser.call(user1)); console.log(await instance.pendingDrawalOfUser.call(user2)); }); });