Skip to main content

Devtools的近期進展和新功能(2017)

· 8 min read

2017/9/22 我和 Ricky Chien 在台灣第一次舉辦的Mozilla Developer Conference中,一起為大家介紹 Firefox 開發者工具,也就是 Devtools 的近期進展和新功能 [^7]。

Devtools 架構的大幅改進,是過去一年中所有 Devtools 貢獻者共同努力的成果。在這講題中,我們可以看到 Devtools 如何持續改進,並提供各種新的工具,讓開發網頁的過程變得更有效率。

透過在 Firefox 57 新版的統一選單介面上開啟 Web Developer Tool,我們可以開啟 Devtools 的各種工具分頁分頁 (Panel)。

Devtools 簡單來說,就是各式各樣網頁開發者工具的集合。這些工具以一個個分頁(Panel)的形式組織在開發者工具區域中,我們稱這個區域為Toolbox,也就是工具箱。

Devtools 的過去​

Devtools 的前身叫做 Firebug [^1]。在 2006 年時,Firefox 的創始者之一 Joe Hewitt 寫出了 Firebug 這個 Firefox 的網頁開發工具 addon [^2]。 使用者可以利用它除錯、編輯、刪改任何網站的 CSS、HTML、DOM 與 JavaScript。也可以透過 addon 再次擴展它的功能。現在各家瀏覽器的網頁開發者工具都參考了 Firebug 功能。

過去的 Web 相關技術遠遠沒有今天成熟,Mozilla 發明了一些特有的技術 (XUL, XBL) 來製作瀏覽器的介面與呼叫特殊的 API (Addon API) 來和作業系統互動。因為原有的 addon 架構無法支援 FIrefox 新的多執行緒 (multiple process) 架構,因此 2016 年 Firebug 開發團隊決定將 Firebug 直接整合進 Devtools 中[^3],原有的 Firebug 開發團隊也大多加入了 Firefox Devtools 團隊繼續貢獻,也因此我和 Ricky 有了和原 Firebug 維護者 Honza 直接共事的機會,在過去半年中我們合作開發 Network Monitor,透過 Daily Meeting 同步進度,也在合作的過程中得到許多難得的經驗。

Devtools 的現在​

Devtools 繼承 Firebug 的開發目標,也就是協助網頁開發者更有效率的製作網站。同時作為網頁開發者工具,我們希望開發者工具工具本身,也能用和網頁開發者使用的排版與框架來撰寫。

於是在 Mozilla Web Summit,大家可以看到我們的成果。我們已經在使用 HTML 和 Web Standard API 來製作 Devtools 開發者工具!

使用 HTML 與 Web Standard API 只是第一步。我們進一步評估,選用了 React 和 Redux 作為前端 UI 介面與狀態管理框架。經過 react 改寫後,我們有了可在不同工具間中重用的元件庫。透過 redux,每個工具也都有了清晰的 App 狀態管理機制。

我們將許多 Devtools 核心的專案放到 Github 上以方便開發者,並且將可以重用的元件,放到 Devtools-Core 專案中,將可以獨立運作的Debugger.html,Perf.html專案也放到 Github 上,讓參與專案變得更容易。

如果想開發自己所需的的開發者工具。現在也可以使用 Web Extension API 來製作新的開發者工具面板。面板中也可以重用Devtools-Core專案提供的 React 介面元件,更快地打造自己的開發者工具。

使用者介面的改進​

Firefox 57 上的 Devtools 分頁變得更美觀了。當然這些都已包成 React component,喜歡還可以拿去用。

對於新配色的選擇,我們的 UX 團隊有做過可用性 (Assessibility) 測試 [^5]。即使是辨色有障礙的人,也可以容易地分辨不同色塊的差異。

Inspector 工具的改進​

Inspector 工具是最常被使用的開發者工具。透過 Inspector,可以查看或修改 HTML。選到任一個 Element 時,可以查看並修改這個 Element 目前所有套用的 Style。

透過重構 Inspector,我們把原本使用的 XUL 與 addon SDK 替換為 HTML 和 web API。並使用 React + Redux 來組織程式的內部架構。

在整理所有 devtools 新增功能的時候,我最先注意到的就是 "Find Useless Rules" 這個超實用的功能。

過去在 Inspector 裡只會在不合法的樣式旁顯示警告標誌,現在我們也會在「合法,但無用」的樣式旁也顯示警告標誌。 比如在 grid 或 flex layout 中使用 float 屬性的話沒有作用,Inspector 就會在 float 屬性旁顯示警告標誌。

此外,Inspector 的 Box Model 會列出所有影響的這個元件位置,大小,或形狀的屬性。透過新的 box model 介面,可以更容易地查看並調整上下左右 position 參數,快速地將版面調整到理想狀態。

Firefox Devtools 也是最先支援 Grid Layout [^4] 的開發者工具。我們可以看到,在 Rules side panel 中的任何修改,都會直接反映到頁面上。

在 Layout Side panel 上會列出了頁面上所有的 Grid Layout。右側則可以選擇要顯示輔助線號碼,區塊名稱,或是要不要延長 Grid 格線以便排版。

不僅如此,在畫面上也可以見到,開發者也可以在 Layout Side panel 中快速的查看對應的網格。我們可以透過這些選項更好地查看頁面上的 Grid Layout。

Web Console​

Web Console 應該是 Javascript 開發者最常用的網頁工具吧。除了查看頁面上的 Javascript 錯誤,印 log 外,Web Console 可以即時執行 javascript,或是存取頁面上的 Global Object。

去年 Web Console 也經過改寫。使用 React + Redux 來組織程式的內部架構。

我們在 console 中可以即時執行 javascript 並取得執行結果。不管是哪種類型的變數,無論是 boolean, array, object 都會用 Object logs 來顯示。

在 rewrite web console 的過程中,我們也將 Object logs 包裝起來變成一個可以重用的 React Component。

這個元件被大量地運用在 Debugger, Network 等其他 panel 上。

如果你想要開發自己的開發者工具或剛好需要類似的元件,reps 可以在 github 上找到。或是透過npm install devtools-reps命令來安裝到你的專案中。

Web console 近期也透過 tree 和 reps 元件,支援了 console group 功能 https://developer.mozilla.org/en-US/docs/Web/API/Console/group

現在透過 web console 實驗 XMLHTTPRequest 或 fetch 時,也可以透過 HTTP inspector 來即時查看 http request 的內容。整套 HTTP inspector 元件是來自 Network Monitor,只經過一點點修改就能完整套用。因此在 web console 中可以直接看到與在 network panel 中一樣的資訊,非常方便。

Perf.html 網頁效能監測工具​

最後我想介紹的是 Perf.html 這套工具。

要提升網頁的效能是相當考驗技術的。 透過 profiling,也就是量測的結果,我們可以更直觀地找出網頁效能的瓶頸並做出針對性地改善。perf.html 就是這樣的量測工具。

去年 perf.html 也經過改寫。使用 React + Redux 來組織程式的內部架構。有自己的網頁,可以脫離 devtools 獨立運作。程式碼 host 在 github 上。

perf.html 使用前需先到網站上安裝 addon,在要量測前後按下 start/stop,就可以得到量測的 profile。

perf.html 最棒的點是,量測好的 profile 可以透過 addon 直接上傳到網站上,我們是在一般網站的介面上查看量測的結果。可以直接分享這個網址,或是標註出關注的時間區間後分享。和其他人一起 trace 效能瓶頸。

經過 photon 更新後,perf.html 介面風格將變得更加簡潔,配色變得更大膽,用起來更舒服。

以上是我分享的部分。若您對 Devtools 感興趣,可以到firefox-dev.tools網站查看所有與 Firefox Devtools 相關的資訊。

參考資料​

用gitbook寫書的體驗

· 2 min read

查看 從 gitbook 遷移到 docusaurus

把過去半年本部落格上關於區塊鏈的文章整理放到 Gitbook。命名為Ethereum 區塊鏈!智能合約 (Smart Contract) 與分散式網頁應用 (DApp) 入門,對區塊鏈,智能合約,分散式應用 (DApp) 感興趣的讀者不妨前往一觀。

這不是我寫的第一本書 [^1],也不是我第一本用 Markdown 寫的電子書 (以前用 Leanpub 出版過Firefox OS 開發書),但絕對是我編輯過程最順暢的一本書(雖然還未完成 XD)。

一路 Markdown​

編輯過程最順暢不是因為對主題很熟悉或寫得快,而是因為從部落格文章初稿到 Gitbook,在寫作的過程中可以一路使用 Markdown。而且由於原本部落格圖片皆使用外連,因此引用圖片時也不用像以前編書時需要重新導入的過程。由於 gitbook 也支援mermaid.js插件,支援我常常使用的 flowchart 語法,因此這些流程圖也不需要重新截圖或繪製,節省了大量時間。

所見即所得編輯器...​

我已有Markdown 格式的初稿,但剛開始我使用 gitbook 提供的所見即所得的編輯器。使用起來感覺非常不自在。

線上編輯器提供的new change request,所見即所得編輯等功能,特別是 gitbook 提供的所見即所得編輯器無法切換回純 Markdown 模式,對於已熟悉 git, Markdown 語法的我來說並沒有變得好用。直接將 Markdown 格式貼到編輯器上時,也無法順利辨識格式,反而是貼上已輸出的部落格網頁時效果好很多。

所以最後我放棄使用線上編輯器,而是在本機編輯 Markdown 後直接 git 推送到專案上。

gitbook 在同步收到新的改動後,會自動編譯並發布新版本,相當方便。接下來應該會繼續使用這個流程。

參考資料​

我如何做個人自動化紀錄-2018版

· 7 min read

2017 年開始紀錄自己使用的自動化紀錄工具,今年更新。

為什麼要做個人自動化紀錄?​

有人會問,Facebook/Twitter 不就可以記錄大小事了嗎?如果會這麼回答,那就實在太天真了😏。FB 上也許包含了自己覺得值得分享的事,但生活中還有諸多事情值得記錄,而不適合與大眾分享。 擁有自己的一份完整數位化生活記錄,是我持續的個人目標之一。要達成這個目標,需要藉助一些自動化紀錄工具,好讓整個過程變得自然而不困難。

照片自動化備份 📷​

  • 360CAM 所拍的相片一律備份到手機
  • Dropbox, 自動從手機上傳照片
  • Google 相簿,充電時自動從手機備份照片到 Google 雲端
  • NAS (Synnalogy), 透過Cloud Sync從 Dropbox 同步照片。

運動自動化紀錄 :walking:​

  • 記步,睡眠紀錄:小米手環 2
  • 體重:小米體重計

現已不再帶小米手環 2,覺得記錄睡眠與步數,並無法改善健康狀況,意義不大。 同時為了降低多走路所需要的意志力,把每日步數改成更容易達成的 300 步,只要開始走,通常都會超過需要的步數。

Update: 四月開始為了減重需要,又再戴起小米手環 2。並將體重計換成體脂計。因應生活型態的改變,行走目標也改為每天 4000 步。


生活事件自動化紀錄​

延續用 IFTTT 做自動生活紀錄這篇的思路,我把看過的書籍、電影,喜歡的 Youtube 影片,貼過的文章,每日完成的事項都記錄到 Google 日曆中,以方便之後回顧。

自動閱讀 / 觀看紀錄 📚​

對於書籍與電影,我使用 RSS + IFTTT + Google Calendar 來自動紀錄。 當我在豆瓣上修改狀態,豆瓣的 RSS 也跟著改變,這時 IFTTT 會將 RSS 中的新事項紀錄到 Google 日曆上。 對於 Youtube 上 like 的影片,Facebook 或 Twitter 上新貼的文章,也會透過 IFTTT 紀錄到 Google 日曆上。

透過 RSS 轉 IFTTT 紀錄

直接透過 IFTTT 紀錄

自動紀錄每日完成的事項 📓​

這部份是自動紀錄的核心。使用 Todoist + IFTTT + Google Calendar 即可達成。 我在 Google Calendar 上使用一個單獨的日曆 (成功日記) 來紀錄每日完成的事項。

If task completed in Todoist, Then log into Google Calendar

從 Email 新增待辦事項 ✉️​

為了更方便地蒐集待辦事項,我參考這份影片 https://youtu.be/V7Dk7pzjJmM?t=11m30s 來將 Todoist#Inbox 設定為 Email 聯絡人,這樣處理 Email 的過程中也能快速地新增待辦事項。

此外也透過 IFTTT,將設為Reference標籤的信件備份到 Evernote 中。

事實上這兩種設定都很少使用。

紀錄看過或待看的網頁 🌐​

我會將待看的文章搜集到 Pocket。

除了瀏覽 Facebook 或 Twitter 上的文章,我也使用 Feedly 訂閱一些自己挑選過的網站。並將 Feedly 設定成當我做標記時,就將本篇文章轉存到 Pocket 稍候閱讀列表,我可以掃過 Feedly 列表,標記感興趣的新聞,稍後再到 Pocket 閱讀。

這樣讓我在看到文章連結當下不需急著看完整篇文章,而是在有空閒的時候才閱讀這些文章。

我唯一的待辦事項收件夾是 Todoist,若看到值得閱讀 (紀錄) 的網頁,桌面上我使用瀏覽器的Pocket外掛插件 (Firefox 瀏覽器內建),將待看網頁記錄到 Pocket 中。

若這個網頁非看不可,我會在按下插件時填入一個自訂標籤fox,然後透過 IFTTT,若發現 Pocket 中新增了一筆含fox標籤的網頁,就新建一筆 Todoist 代辦事項。

在手機上就直接使用 Todoist 和 Pocket 等 App 達到一樣的效果。

文章更新時自動提醒 ⏰​

有些網站並未提供 RSS 訂閱,手機上我會使用Web Alert來取得網頁更新提醒。

開發工具設定自動備份 ¨​

使用 VS Code Settings Sync ,只需剛開始時設定一次,之後可同步各種 VS Code 中的設定與插件。


自動化網站部署 🌐​

目前已使用 Github 來放我的個人網站與部落格,透過與 Travis CI 整合,我所修改的任何內容,在幾分鐘之內都會自動部署到網站上。

如何做可參考 Hello Hexo (個人網站自動化部署) 和 Automatically deploy new commit to github pages via Travis CI

Auto website deploy flow

一些可以直接運作在瀏覽器的專案 (如 BlocklyDuino 和 Saihubot),我會直接將 gh-pages 設為預設分支,所有改動直接 push 到這分支中。這樣一有改動即可在網頁上看到更新成果。


半自動紀錄​

半自動工作紀錄 💼​

透過翻看 Todoist 或 Google Calendar,我可以輕易地將過去一週達成的事項整理出來,再送 PR 到 Github 上。 也可以說這塊目前只能算半自動化地列出過去事項列表,可以再繼續改進。

定期回顧與整理​

我在 Todoist 中增加一個Template項目,裡面放了周檢視 / 月檢視 / 季檢視 / 年檢視樣板,透過重複時間設定,每段時間自動提醒該做檢視了。

撰寫本文的目的之一,也是讓我有回顧我的自動化運作的機會。

照片備份規則​

由於 Dropbox 空間有限,會不定期將 Dropbox 上的照片移動到到 NAS 上按年月份分類的photo/資料夾.

我的照片並不算多,但若有出遊的月份通常照片會暴增。所以我的基本備份規則是依年份,並以雙月份命名資料夾,若是當月有重大活動則直接在檔名中標注。 例如 2016 年的照片資料夾裡會有2016_10_11,或是2016_06_london這樣的命名。

在整理照片的時候,每當遇到特別喜歡的,我會另存到 Dropbox 中的一個依年份歸檔的資料夾,例如 2017 年的精彩照片我會另存到 dropbox/spot/2017資料夾中,這樣隨時可以找出來欣賞。

另外每年累積的一些螢幕截圖,也放在當年度的screenshots資料夾裡。

清理 RSS Feed​

透過 Feedly 訂閱 RSS Feed 太容易,但是不小心每天收到的新聞量就遠高於自己能吸收的量,這時可以到 https://feedly.com/i/organize/my 把那些失效的連結清掉,並快速檢視一下現在仍在訂閱的網站,是否還對這些主題感興趣。

手動紀錄​

為了平衡日常太依靠電子產品的趨向,前年開始就嘗試使用實體筆記本作一些紀錄,2018 一月中開始嘗試養成更頻繁地使用實體筆記本的習慣。在幾經調整後,目前我使用 B5 方格筆記本做基礎,搭配不同的魔擦筆來作筆記。實體筆記本的好處是除了一般的紀錄,還可以隨意畫心智圖,黏照片,貼紙,蓋印章等。參考各種筆記術書籍,我在每本筆記本前幾頁會空出索引區,將筆記本內容索引起來,以便之後查找。

暫時還沒想好(也還沒需求)該如何數位化。

參考資料​

個人工具箱2018二月

· 4 min read

先記錄下來手邊使用的工具,才有機會從繁雜中歸納出簡單的使用規則。

2011 [^1] 年時紀錄了一次當時的個人工具箱,2016 年開始再次紀錄自己手邊工具箱的改變 [^2] [^3] [^4]🤹。 今年初工作上有了變化,也將家中的環境包含進配置列表中。

主要裝備​

  • :computer:Dell XPS 13 (8550), Windows 10, i7 4GHz x4, 8GB RAM, 13.3" LCD,作業系統:Elementary OS + Windows
  • 📱One Plus 3, Android, 6GB RAM, 5.5" AMOLED
  • :headphones:Sony WI-1000 降噪耳機

家​

  • 🖥24" LCD
  • ⌨羅技 K375s 鍵盤
  • 🖱 羅技 M720 滑鼠
  • :package:Synology NAS: 電影,影集,照片,資料備份
  • :bulb:Yeelight LED 燈泡
  • 備用 :computer:Macbook Air 13", 2012 年版
  • 備用 :tv:Chromecast 一代
  • 備用 :speaker:Echodot
  • 備用 Respberry Pi 3
  • 備用 :orange_book:Kindle Voyage

和去年相比的改變:​

主力用:computer:Dell XPS 13",家中添購了一台🖥24" LCD 接筆電,搭配 3 轉 1 HDMI Hub,接🎮Nintendo Switch, Chromecast, Respberry Pi,可滿足影音相關需求。HDMI 設備通電時會自動切換到對應的設備。

上次記錄前即開始使用的⌨羅技 K375s 鍵盤與🖱羅技 M720 滑鼠,兩者特點都是同時支援使用 USB 或藍牙控制設備,讓我可以用一套鍵鼠控制多種設備。

🕸網站工具​

設計工具​

開發工具​

  • 編輯器:VS Code
    • Blank Line at the End
    • Diff Tool
    • EditorConfig for VSCode
    • Go
    • Guides: more guide line
    • GitLens: more git info
    • Python
    • Settings Sync
    • solidity
    • Trailing Spaces
  • 版本控制:git
  • 套件管理:apt (Linux), nvm, yarn, Chocolatey(windows)

若需要在 Windows 上開發,能用 Chocolatey 處理的就用 Chocolatey 處理。

娛樂工具​

  • 🎮Nintendo Switch
  • :tv:Chromecast (少用)
  • 🎮Steam /vlc (少用)

因為攜帶便利,現在主要只玩 NS 上的遊戲。Steam 上看到喜歡的遊戲,也盡量等 NS 上出了再買。

:iphone:Android 手機應用​

  • 瀏覽器:Firefox for Android
  • 網頁更新通知:Web Alert
  • 閱讀:Feedly, Readmoo
  • 稍候閱讀:Pocket
  • 信箱:Inbox, Gmail
  • 地圖:Google 地圖
  • 記事:Keep
  • 內建相機,內建時鐘
  • 相簿:內建相簿,Google 相簿,Dropbox
  • 社交:Facebook, LINE, Twitter
  • 待辦事項:Todoist
  • 密碼管理:Bitwarden
  • 記帳: Toshl
  • 線上影片:Youtube
  • 有聲書:Audible
  • 音樂:豆瓣 FM
  • 健康:小米運動,Pokemon Go, 7Min workout

工具

  • 2 步驟認證:Duo Mobile
  • 旅遊:Google 翻譯,TripAdvisor
  • 影片播放:MX Player, DS video
  • 記錄看書狀況:Readmoo 分享書
  • 線上學習:Udemy, EggHead.io
  • IFTTT
  • 💳街口支付

Reference​

如何撰寫智能合約(Smart Contract)?(IV)加入單元測試

· 6 min read

因為智能合約一旦部署就難以修改,因此合約的安全性極其重要,要避免合約中出現一些基礎錯誤,除了透過第三方驗證外,完整地單元測試 (unit test) 也是必需的。

目前最成熟的智能合約單元測試方式,還是透過Truffle開發框架來達成。有趣的是 Truffle 主要使用 Javascript 來撰寫智能合約的單元測試(也可以用 solidity 來寫)。

加入測試​

接續上一篇建立的HelloToken合約,在test/目錄下加入test_hello_token.js測試檔案(如果覺得這份程式碼不易理解,可跳過這節,後面會介紹更簡潔的測試方法,到時再回來對照著看)。

var HelloToken = artifacts.require('HelloToken');

const INITIAL_SUPPLY = 100000;
let _totalSupply;

contract('HelloToken', function(accounts) {
it('should met initial supply', function() {
var contract;
HelloToken.deployed().then((instance) => {
contract = instance;
return contract.totalSupply.call();
}).then((totalSupply) => {
_totalSupply = totalSupply;
assert.equal(totalSupply.toNumber(), INITIAL_SUPPLY);
return contract.balanceOf(accounts[0]);
}).then((senderBalance) => {
assert.equal(_totalSupply.toNumber(), senderBalance.toNumber());
});
});
});

運行truffle test可看到測試通過的結果。

Contract: HelloToken
✓ should met initial supply

1 passing (11ms)

講解​

var HelloToken = artifacts.require('HelloToken');

artifacts.require的用法和在migrations/中的用法相同,可以直接引入對應的智能合約。

contract('HelloToken', function(accounts) {
it('should met initial supply', function() {
});
});

Truffle 是使用 Javascript 開發中常見的Mocha測試框架和Chai斷言庫來做單元測試。差別只是把 Mocha test 中的 describe換成contract。根據官方文件 [^1],contact執行前會自動重新部署到 testrpc (或測試網路) 上,所以智能合約會是剛部署好乾淨的狀態。

此外,contract也會帶入accounts變數,裡面儲存了 testrpc 或其他你運行的測試網路所提供的帳號,開發者可以直接使用這些帳號來測試合約。

第一個測試是來測部署合約後預設的代幣數目是否正確。

var contract;
HelloToken.deployed().then((instance) => {
contract = instance;
return contract.totalSupply.call();
}).then((totalSupply) => {
...
});

這邊內容和在truffle console中輸入的測試內容雷同,使用Promise來確定每個非同步的操作都在上一個操作完成後才繼續執行。

上一個操作可以透過 return 語句回傳下個操作需要的參數。例如這邊then裡面傳入的totalSupply參數,是來自上一行return contract.totalSupply.call()的結果。

assert.equal(totalSupply.toNumber(), INITIAL_SUPPLY);
...
assert.equal(_totalSupply.toNumber(), senderBalance.toNumber());

這邊我們透過assert.equal語句驗證了HelloToken合約中的初始代幣總額與INITIAL_SUPPLY參數的值相符,且與合約部署者 (accounts[0]) 帳戶中擁有的總額相符。

使用 async/await 簡化測試​

要理解這樣的 promise chain 需要一些練習。但其實上面的測試用例中,我們只想做好最後的兩個 assert 驗證。有沒有比較直覺的測試方法呢?

有的!2017 下半年,Javascript 語言支援了async/await語句 [2](只要安裝 Node 7.6 版以上即可使用),可以用更直覺的方式撰寫非同步的程式碼。

智能合約測試剛好也使用大量的非同步程式碼。使用async/await語句改寫後的智能合約測試程式碼如下:

var HelloToken = artifacts.require('HelloToken');

const INITIAL_SUPPLY = 100000;

contract('HelloToken', function(accounts) {
it('should met initial supply', async function() {
let contract = await HelloToken.deployed();
let totalSupply = await contract.totalSupply.call();
let senderBalance = await contract.balanceOf(accounts[0]);
assert.equal(totalSupply.toNumber(), INITIAL_SUPPLY);
assert.equal(totalSupply.toNumber(), senderBalance.toNumbe());
});
});

運行truffle test可看到測試通過的結果。

Contract: HelloToken
✓ should met initial supply

1 passing (11ms)

講解​

it('should met initial supply', async function() {
});

要在程式碼中使用 async/await,需要在函式前加入async宣告,這樣解譯器才會解析函式中的await語法。

let contract = await HelloToken.deployed();
let totalSupply = await contract.totalSupply.call();
let senderBalance = await contract.balanceOf(accounts[0]);

透過在非同步的操作前加上await宣告,這三行程式會依照順序,等待第一行 await 語句執行完,取得contract變數後,再依序執行第二行語句。第二行語句執行完,取得totalSupply變數後,再繼續執行第三行語句以取得senderBalance變數。

後面兩個 assert 語句則與 promise 撰寫時完全一樣。這樣改寫後,程式碼的可讀性大大地提昇了!

加入轉帳測試​

再透過async/await語句試著加入轉帳測試:

it('should have right balance after transfer', async function() {
const AMOUNT = 123;
let contract = await HelloToken.deployed();
// check init balance
let account0Balance = await contract.balanceOf(accounts[0]);
let account1Balance = await contract.balanceOf(accounts[1]);
assert.equal(account0Balance.toNumber(), INITIAL_SUPPLY);
assert.equal(account1Balance.toNumber(), 0);
// check balance after transferred
await contract.transfer(accounts[1], AMOUNT);
account0Balance = await contract.balanceOf(accounts[0]);
account1Balance = await contract.balanceOf(accounts[1]);
assert.equal(account0Balance.toNumber(), INITIAL_SUPPLY - AMOUNT);
assert.equal(account1Balance.toNumber(), AMOUNT);
});

運行truffle test可看到測試通過的結果。

Contract: HelloToken
✓ should met initial supply
✓ should have right balance after transfer (92ms)

2 passing (151ms)

講解​

let account0Balance = await contract.balanceOf(accounts[0]);
let account1Balance = await contract.balanceOf(accounts[1]);
assert.equal(account0Balance.toNumber(), INITIAL_SUPPLY);
assert.equal(account1Balance.toNumber(), 0);

範例的前半部測試帳號0與帳號1中的代幣餘額。帳號0即部署代幣的帳號,因此擁有所有的HelloToken代幣,而帳號1中則沒有HelloToken代幣。

await contract.transfer(accounts[1], AMOUNT);

接著呼叫合約的transfer方法將一些代幣轉入帳號1。注意這些都是非同步的操作(送出傳輸命令後,要先等待區塊鍊確認),因此需要使用await語句。

account0Balance = await contract.balanceOf(accounts[0]);
account1Balance = await contract.balanceOf(accounts[1]);
assert.equal(account0Balance.toNumber(), INITIAL_SUPPLY - AMOUNT);
assert.equal(account1Balance.toNumber(), AMOUNT);

範例的後半部再次測試帳號0與帳號1中的代幣餘額。結果符合轉帳後兩個帳戶的預期代幣數額。

結語​

async/await語句相當適合拿來寫非同步的程式,這特性太適合用來寫智能合約的測試了。因為async/await這語法太新,所以大部分的參考資料都還在用Promise來撰寫。我建議當你看到相關的智能合約測試時,可以用 async/await 改寫看看,會有很不一樣的感受。

參考資料

[1] Writing Tests in Javascript http://truffleframework.com/docs/getting_started/javascript-tests [2] 6 Reasons Why JavaScript’s Async/Await Blows Promises Away (Tutorial)https://hackernoon.com/6-reasons-why-javascripts-async-await-blows-promises-away-tutorial-c7ec10518dd9