2015/9/19

調整 Android Studio 編譯檢查規則

在 Android Studio 中進行程式碼的編輯時,會對程式碼進行即時的分析,並且在編輯窗格的右上方顯示分析的結果。檢查程序在進行中時,窗格右上方的角落會以一個眼睛的圖示來表達。如果程式碼通過所有檢查條件會顯示綠色的勾勾;程式碼有違反一項以上的警告規則顯示黃色的方塊;當程式碼有違反一項以上的錯誤規則顯示紅色的圓圈包著驚嘆號。

很多人在寫程式的時候都只關心編譯後能不能執行好進行測試,因為程式一旦有錯誤就會中斷編譯程序、阻擋工作的進行,要等到錯誤的程式碼被修正後才能繼績工作。而警告的訊息並不會阻擋工作的進行,就會變得無所謂、大多數的人都會選擇不予理會。

這並不是一個好的開發習慣,Android Studio 進行的程式碼分析就像健康檢查一樣,用來顯示程式碼的健康程度。當健康檢查的報告中有數值超出標準值,雖然沒有達到疾病的程度,但代表身體已經有一定的問題。如果繼續忽視不理,很有可能就會轉變成疾病,甚至危害生命。同樣的道理,當分析的結果出現警告的訊息代表程式的寫法有一定程度出錯的風險。所以一但有警告訊息出現,應該要和錯誤一樣認真地看待並謹慎地處理。

不過,有的時候特定的習慣性寫法或是團隊的 Coding Convetion 可能會和工具預設的檢查規則有衝突,但其實並不影響程式的執行結果。舉例來說,以下的程式碼是一般判斷布林值的慣用寫法:


但是這樣的寫法由於「!」的寬度不寬,和判斷布林值是 true 的程式碼差別沒有很大,很容易因為看漏而導致修改程式時誤判而發生改錯程式碼的情況。再者,我們的母語並不是英文,所以在命名變數時常常詞不達意,甚至在過去的工作經驗中遇到有人命名變數的語意與用途相反的情況,更大大地增加了這樣的情況出現的機率。

為了有效地提高防呆的機率,我都習慣性地使用以下的方式來撰寫判斷式:


雖然會多打好幾個字,但是卻可以換來表達意思明確的效果。閱讀程式碼的人容易判斷區塊中到底是要執行 true 還是 false 的邏輯,不會被變數的名稱誤導形成邏輯上的混亂。

但 Android Studio 預設的檢查規則在遇到以上的寫法卻會顯示以下的警告訊息:

flag == false can be simplified to !flag

Reports pointless or pointlessly complicated boolean expressions. Such expressions include anding with true, oring with false, equality comparison with a boolean literal, or negation of a boolean literal. Such expressions may be the result of automated refactorings not completely followed through to completion, and in any case are unlikely to be what the developer intended to do.


當這樣的訊息與其他的警告混在一起就會造成在寫程式時的困擾,因為沒有辦法在第一時間就利用編輯窗格右上角的綠色勾勾,了解程式是否已經通過其他的檢查規則。當看到窗格的右上角呈現黃色的方塊,如果要確認還要將滑鼠游標移到有問題的位置,才能夠判斷是不是因為以上的程式碼寫法所產生的警告訊息,或是還有其它的問題待修正!

這樣的動作如果在寫程式時頻繁的重複,會讓工作的效率下降很多,而且也會因為久了出現怠惰的心態,進而忽略警告訊息造成程式出問題的風險增加。

所幸 Android Studio 的檢查規則是可以調整的,在 Settings 的對話視窗中選擇「Editor->Inspections」項目,就可開啟或關閉特定的檢查規則。以文章中的示範程式碼寫法為例,可以切換到「Java->Control flow issues->Pointless boolean expression」項目。如下圖所示取消勾選該項目,Android Studio 就不會再針對這項寫法進行檢查。



有更動過的選項會以藍色的字體顯示,同時所屬的逐級向上分類項目也會顯示藍色,以方便識別。


2015/9/4

如何在 Android Studio 中批次執行不同性質的測試程式

當所開發的系統中,測試程式被依性質做區分,例如:單元測試(Unit Testing)、整合測試(Integation Testing)。通常會讓整合測試交給後端的 CI 系統來執行,因為相較於單元測試,整合測試所需的執行條件較高、所耗的時間也相對地較長。否則隨著程式日漸龐大,很有可能會出現只是幾分鐘的程式修改,但等測試執行完成卻要花上數倍時間的情況。

而單元測試就會被程式撰寫人員執行,用來確認程式是否依設計的內容來完成,所以這個時候就會需要有一個簡易的方法可以用來觸發單元測試的執行。在 Android Studio 中可以設定 Configuration 以便用來觸發不同的執行內容,像是要執行測試可以選擇新增 JUnit 或 TestNG  設定來啟動測試。

以 JUnit 為例,如果要在一堆的測試程式檔中只執行特定的測試項目,最直接的方法就是把檔案用不同的目錄或是 Package 分隔,在設定 Configuration 時指定對應的目錄或是 Package,就可以在測試啟動後只執行其下的測試檔案內容。

只是有的時候專案的結構較為複雜,因為規劃的關係同性質的測試檔案沒有辦法都歸在同一個目錄或 Package 下,更進一步可能檔案在不同階層的目錄或 Package 間交錯。而 Android Studio 的 JUnit Configuration 同時只能指定一個目錄或 Package,就算是可以指定多個,要一個一個的設定不但麻煩還會有遺漏的問題。當開發還在進行、專案結構還持續有異動時,要維持設定的正確更是個挑戰。

這時如果測試檔案的名稱有一定的規則,例如:單元測試以 UT 做為檔名的結尾、整合測試以 IT 做為檔名的結尾,可以使用 JUnit Configuration 裡的另一個選擇 - Pattern。不過這個部份在官方文件中沒有找到比較明確的說明,只有簡略地提一下可以透過搜尋由清單中選出所需的檔案。

實際上可以如下圖所示,在 Pattern 右方文字框中輸入 Regular Expression 內容,像是要過濾以 UT 結尾的檔案可以輸入 ^(.*UT$).*$。啟動後會依設定的搜尋範圍來進行比對,挑出檔案名稱符合樣板的測試檔案,並執行所有符合條件的測試程式。



2015/8/27

在 Spock 測試方法間共用程式碼

在進行測試程式開發時,經常會有執行或驗證的程序是在不同的測試案例間一直重覆,當然使用 Spock 時也不例外。根據 Spock 的官方文件提到,在 Spock 中可以使用 Helper Methods 來把要被共用的程式碼收納於其中,並且在不同的測試方法間呼叫。

以下為測試方法共用程式碼的範例:


而驗證的部份也可以使用相同的模式來實作,以下是根據官方文件所建立的程式碼:


在以上程式碼第 25 行的 Helper Method 中加入 assert 依據官方文件的說明是為了確保在測試結果輸出時,可以顯示是哪一行驗證結果不符合預期。而照實作的經驗,如果把 assert 移除,發生驗證失敗時不會顯示訊息,甚至顯示測試通過的結果。

這裡有個細節要注意的是 Helper Method 必須要明確的以 void 宣告無傳回值,否則 Spock 可能會依據 Helper Method 的內容解譯產生不符合預期的結果。另外有一個小差異是在 Android Studio 裡,如果使用 '&&' 的語法串起所有的驗證指令,沒有辦法像官方文件所示範的把 '&&' 放在程式碼的最開頭,必須要把 '&&' 放在程式碼的結尾後斷行,否則會顯示語法錯誤的訊息。



2015/8/23

Android Studio dose not highlight task tags

A couple days ago, I upgraded Android Studio to 1.3.1 from 1.3 in OS X 10.10.4. After Android Studio was reopened by upgrade program, I found all the TODO and FIXME task tags were not highlighted. They looked just as normal comments and not listed in TODO Tool Window.

It really annoyed me when I can't identify these task tags at first sight in my code. I use task tags for my suspended work and they can remind me what I still not done. I really don't want to use 'Find' to confirm if I clear all the task tags, it's not smart.

So I opened Settings dialog and tried to find out what was going wrong. In the TODO page, I found cause. All the TODO patterns were cleared and made my custom filters turned into red.


I put these two default patterns as below back into list:
  • \btodo\b.*
  • \bfixme\b.*
Yes! I got the highlight back. But, after I reopened Android Studio, the pattern list was empty again! So I didn't solve this issue actually. Dose the issue come along with 1.3.1? There was another possibility. I just added custom filters before I upgraded. Since I didn't have Android Studio 1.3 to verify my guess. I decided to delete all the filters in the list. The issue dose not show up any more. I think this is a bug of Android Studio.

But I don't like this solution. I use FIXME and TODO to distinct different tasks. The tasks must be done before go live are tagged with FIXME. The rest of them are tagged with TODO. This solution means I can't switch the list between different priority tasks and focus on them.

To get better solution, I tried many combinations of patterns and filters in TODO page. There is a workaround after many times reopened of Android Studio. It is quit simple, just add a custom pattern into list and set filters what I did. I got the highlight and filter both.

2015/8/22

如何使用 Spock 測試 Static Method

使用 Spock 來開發測試程式除了在之前的文章中提到的:可輔助 BDD 的開發流程、與 JUnit 相容及內建 Data Driven 功能等特性之外,還有另外一項優點是內建了 Mock 的功能。Spock 所提供的 Mock 功能在使用上相當的簡潔,以下是一段範例的程式碼:


由以上的程式碼可以看到只要使用 Mock() 函式就可以對指定的對象進行 Mock 的程序,同時也提供二種彈性的宣告方式,一是把要 Mock 的對象傳入 Mock() 函式(第 3, 4 行),另一個方式是使用 Mock() 函式來建立執行個體(第 5 行)。第二種 Java-like 的語法主要是希望在編輯程式碼時 IDE 可以提供較好的支援,執行時會以宣告的型別來判定要產生的 Mock 對象之 Instance。

Spock 所提供的 Mock 功能有另一個亮點是不僅能夠針對 Interface,同時也可以使用在一般的 Class 上。如此在進行系統設計時,就可以不用遷就「是否有辦法分開測試」這個問題而要使用大量的 IoC 在設計之中,以致形成了過度設計的情況。在 Mock Class 前,專案必須要引用 cglib-nodep 和 objenesis 這二個函式庫,所以在 build.gradle 的 dependencies 應該像以下所示範的內容:


根據官方文件的說明,如果要驗證目標 Class 與其週邊 Object 互動的過程是否符合像是循序圖所表達的設計內容,可以使用範例程式中所示範之內容。使用的語法規格是:[互動的次數] * [互動的標的],1 * mockClass.start() 就是驗證 Processor.run 是否有呼叫 ConcreteClass.start(),而且整個 Processor.run 的執行過程中只呼叫一次。

從範例程式中還可以看到,Spock 所提供的 Mock 功能,除了可以用來驗證 Class 間互動過程,同時也具有 Stub 的能力。可以依據呼叫的內容來提供對應的動作,增加了測試程式的靈活性。像範例程式中的第 8 行,當有程式呼中 mockInterface2.getInfo() 時,就會固定傳回 "二號測試字串" 的內容。而在第 15, 16 行,是代表當 Processor.run 的執行過程中,有呼叫到這二個目標函式時所會取得的內容。

雖然 Spock 提供了許多優異的特性,不過,美中不足的地方是目前的版本沒有辦法 Mock 靜態函式。在網路上看了一些討論之後,PowerMock 似乎是一個可用來搭配 Spock 解決這個問題的框架。進一步了解發現 PowerMock 所提供的套件種類繁多,包含了與幾個主流的測試框架整合的功能。而如何在 Gradle 中設定對應的套件,可以讓 Spock 與 PowerMock 協同運作會是一個門檻。

基本上要在 Spock 中使用 PowerMock 來 Mock 靜態函式,會用到 JUnit、Mockito 和 PowerMock 這幾個框架,這使得在設定 build.gradle 內容時更加的棘手。而在測試程式中要把這四個框架整合起來,以便能夠達到 Mock 靜態函式的目的,也是很費心神的一件事。

目前網路上查到的資訊都是片斷的,所以在這裡把相關的資訊統整起來,做為日後研究的起點。以下是 build.gradle 的內容:


以上的套件應該要依照所屬框架更新的狀況來調整對應的版本編號。

在測試程式中,如果有一個以下待測的 Class:


測試程式應該像以下所示範的內容來進行靜態函式的測試:





2015/8/19

解決 Android Studio 中 TODO Highlight 效果消失的問題

前二天在昇級 Android Studio 到 1.3.1 之後,發現了一個奇怪的問題。由於更新後 Android Studio 需要重啟,但在 Android Studio 重啟後,所有 FIXME 及 TODO 的註解無法呈現 Highlight 的效果,同時在 TODO 的 Tool Window 中也搜尋不到任可的 TODO 項目。

在檢查過相關的設定之後,發現 TODO 的 Pattern 設定都被清掉了,難怪沒有辦法顯示 Highlight 的效果,也搜尋不到任何的 TODO 項目,甚至連原本設定的 Filter 都因為失效而顯示紅色。



因為原本就沒有定義額外的 Pattern,所以依照官網的說明,將以下二個預設的 Pattern 加回去後就恢復正常了。
  • \btodo\b.*
  • \bfixme\b.*
如果是像我一樣有自己新增 Filter 的設定,在加完 Pattern 後還是要再重新修改 Filter 的內容。

事情本來應該就這樣結束了,但其實案情並不單純,就在某個需要下重啟 Android Studio 後,TODO 的所有 Pattern 設定內容又被清除了!

由於這個功能在我進行開發工作時扮演了重要的角色,只好先放下手邊的工作研究一下。之所以重要是因為在開發的時候不一定可以完美地依照需求或設計的順序輸入程式碼,有可能會需要把繕打到一半的程式碼擱著,先去完成其他部份。或是有可能需求或設計還不確定,先以仿造的邏輯來輸入程式碼做為替代,等不確定的部份有明確的結果後再回來調整程式碼。

然而,在此時如果沒有一個有系統的方式來標注程式碼,完全依靠記憶力來找到這些待修改的程式碼,是一個非常不可靠的方式。就算是以重新檢視全部程式碼的方式,也有可能因為分神而漏看,再者程式的規模大到一定的程度,這種方式就變成了一種精神上的折磨。

故意讓程式編譯失敗是一個方法,但程式就會一直處於無法藉由執行來確認程式是否正常運作的狀態。反之,如果當初擱下的程式碼沒有編譯上的問題,就有可能因為開發的負責人忘了修改,造成程式上線一陣子了,才發現程式其實沒有完成就交付給使用者、更嚴重的可能已經造成了大量資料的錯誤。這在一般的軟體開發案子中並不罕見,因為很多的案子是沒有足夠的資源做後續的驗証與測試,以簡單的 UAT 做把關、沒有明顯的問題就放行了。

在註解中使用 FIXME 或 TODO 是解決這項問題的好方案,可以用文字來說明程式碼有問題的地方或是待完成的工作,絕大部份的 IDE 也都提供了 Highlight 的功能。比故意讓程式編譯失敗要有人性,不用費神去回想為何編譯會失敗、程式要如何改正。是可以有效地在開發階段的初期發現問題、降低系統運作問題的風險。當然,這也是要負責程式碼的人有養成良好的習慣才會有效果,所以這個習慣應做為建立團隊文化的重要規則之一。

有了好習慣,一旦沒有了 Highlight 的功能,就沒有辦法很直覺地找到還有問題的程式碼片斷,同時因為 Tool Window 也失效、列不出清單,只能以搜尋的功能來替代,對工作效率來說是有不小的影響。

印象中之前重啟 Android Studio 時並沒有出現同樣的問題,這個情況似乎是在我設定 Filter 之後才發生。我習慣把在上線前一定要修改的項目以 FIXME 標注、可以等上線後再思考解決方案的項目以 TODO 來標注。所以會需要在 TODO 的 Tool Window 中過濾 FIXME 和 TODO,以便了解在上線前還有多少部份的程式碼是沒有完成的,讓我可以專注在 FIXME 的項目上。

根據這個需求我設了二個 Filter 分別只勾取其中一個 Pattern,讓我可以在 TODO 的 Tool Window 來切換不同的清單。為了確認是不是設定 Filter 所產生的問題,我就將 Filter 全數刪除了之後重啟 Android Studio,果不其然 Pattern 這次就沒有被清除掉了,這應該是一個 Android Studio 的 Bug 吧!

但如果是為了解決重啟 Android Studio 後不清除 TODO Pattern,那不就沒有辦法使用 Filter 了嗎!?  好吧!秉持著研究的精神,就在 TODO 的設定畫面中試了幾個不同的 Pattern 及 Filter 的組合,總算發現了一個 Workaround!

原本如果只有預設的二項 Pattern,一旦設定了 Filter 就會有重啟後被清除的現象。但是在預設的二項 Pattern 外再加上一項自訂的 Pattern,依我原本的條件設定 Filter 就可以在重啟 Android Studio 之後保有重啟之前的 Pattern 設定。

目前我工作的環境是 OS X 10.10.4 及 Android Studio 1.3.1,有類似問題的朋友可以參考看看。


2015/6/21

使用 Android Studio 開發 Web 程式 - 測試

測試

這是「使用 Android Studio 開發 Web 程式」系列的最後一篇文章,接著前一篇偵錯的主題之後要進行的是測試。不是已經可以偵錯了? 偵錯就是在測試了啊? 還要研究什麼? 有這樣疑問的人可以先參考一下這一篇文章

研究的方向是希望能夠確認在 Android Studio 進行測試時,可以滿足下幾項測試上的需求:
  • 自動化
    不僅僅是在開發時能夠大幅減少測試這項工作的負擔,並且還要能夠把自動化延續到 CI 系統中,以便有效提昇整體的開發工作品質。
  • Code Coverage
    作為測試完成度的一項指標,藉以調整測試程式開發的策略、投入的成本及風險的控管。
  • Data Driven
    用來與測試的策略搭配以提昇 Code Coverage,並且可以將造成系統運作問題的資料保留下來,成為一個收集系統問題的機制,成為回歸測試的基準之一。

在 Android Studio 裡就已內建了對 JUnit 和 TestNG 的支援,所以只要按照這些 Framework 的規格寫好測試類別,並且把檔案存在對應 Module 的 src/test 目錄之下,IDE 就會自動識別出來。IDE 也有提供對應的功能來簡化測試的產生程序,相關的細節可以參考官網的 Creating TestsCreating Test Methods

有了測試的程式,接下來要確認是否能夠達成自動化的需求。首先,要啟動測試的話,透過 IDE 就可以輕易的達成,執行的細節可以參考官網的 Creating Run/Debug Configuration for TestsPerforming Tests 的說明文件。測試的結果會直接顯示在 IDE 的介面上,但要先確定「Show Statistics」的選項是否開啟,否則只會看到執行後的輸出結果。要如何開啟「Show Statistics」選項可以參考官網 Viewing and Exploring Test Results 的說明文件。

JUnit Configuration

由 Run/Debug Configuration 的設定畫面可以發現,和 Eclipse 相同,可以設定要執行測試的範圍。所以只要設定好適當的選項,即可在 IDE 中一鍵執行所有的測試來達成開發階段的測試自動化。與 CI 系統整合的部分,既然是 Gradle 的專案,當然是執行 test 或是有包含 test 的 Task 來達到目的,由相關的功能來看自動化的需求已經能夠滿足。

編寫程式碼的系列文章中有提到,IDE 的介面上就可以直接執行 Gradle 專案的 Task,和在 Terminal 下指令相同,都會產生 HTML 格式的測試報告在對應 Module 的 build/reports/tests 目錄下。但如果是在 Terminal 下指令則要自行用瀏覽器開啟測試報告的 HTML 檔,而使用 IDE 執行 Task 則可以在 IDE 中檢視測試的結果,並點選如下圖所示的「Open Gradle test report」圖示啟動並顯示測試報告的內容。

Open Gradle test report

而與測試有著密切關連的 Code Coverage 需求,由於 Android Studio 內建就有整合了 Code Coverage 的功能,所以只要是屬於測試類型的 Configuration 項目都可以使用「Run 'xxx' with Coverage」的功能,如果不是測試類型的 Configuration 項目在 Toolbar 上的圖示會被 Disable。相關的操作細節可以參考官網的 Running with CoverageViewing Code Coverage Results 的說明文件。

最後一項需求是 Data Driven,在 Android Studio 所支援的 JUnit 和 TestNG 都有提供特定的方法來達成目的。但在這裡有一個比較不一樣的選擇:Spock,對,就是那個尖耳朵、瓜呆頭的半 Vulcan 星人的名字。這是一個和 JUnit 相容的測試框架,也就是說可以無縫式地被整合在支援 JUnit 的 IDE 中,再說得白話一點就是可以在 Android Studio 裡使用 JUnit 的 Configuration 項目執行 Spock 的測試程式、產出測試報告、執行 Code Coverage 等。

為什麼為想要使用 Spock 來做為測試的框架?主要是長久以來軟體設計的生命週期裡,需求和程式之間都有一道明顯的鴻溝,原因是主流的程式語言還沒有進化到可以使用自然語言來表達,所以就需要靠程式撰寫人員來轉譯,把需求文件中的文字轉成可執行的程式碼。就算是導入 TDD 來協助開發,程式碼可以透過先寫好的測試程式來檢查是否符合需求,然而目前的測試程式也還都是相同的程式語言所構成的,需求裡描述的情境還是要再被轉一手。

轉換就會有出現落差的風險,但是 Spock 使用 BDD 的概念填補了需求文件和測試程式間的差距,讓測試案例可以不被大幅轉譯的情況下,在測試程式碼中以接近自然語言的語法來呈現(可惜是當然地使用英文的語法),使轉譯失真的風險有效地被降低。就 Spock 這項特色來說,可以讓文件和測試建立直接的關連並且用來驗證實作的結果,是相當具有吸引力的。當然,Spock 也可以使用 Data Driven 的方式來撰寫測試程式,而且能夠以更直覺的的式來建立測試時所需的資料。

Spock 的測試類別是以 Groovy 為基礎,要在原本是 Java 的 Module 上使用 Groovy 要先設定讓 Module 支援 Groovy,詳細的操作步驟可以參考官網的 Configuring Groovy-Based Frameworks 說明文件。並且在 build.gradle 裡應該包含以下示範的內容:


在使用 Data Driven 產出的測試報告時有一個要注意的細節,通常測試報告顯示的單位都是以測試項目為基準,但由於 Data Driven 是在一個單一的測試項目中重覆地輸入不同資料的方式來進行。在使用 Data Driven 產出的測試報告沒有辦法顯示每一個受測資料樣本的測試結果時,會造成閱讀報告時一個很大的困擾。因為使用的樣本數有可能成百上千組,當測試報告顯示某個項目測試失敗,但卻無法顯示造成失敗的資料樣本,甚至連失敗樣本組數都沒有的話,錯誤根本就無從定位,也沒有辦法被修正。

在 Spock 中提供了一個 @Unroll 的 Annotation,可以讓測試報告顯示的項目揭露到每一組資料樣本的層級。透過 Gradle 的 test Task 產出的報告檔就會根據這個 Annotation 調整內容,將原本以測試項目為單位改為以受測的樣本組為單位。

但在 Android Studio 裡會有一個小狀況是,如果只單獨執行一個測試類別,在 Statistics 裡可以檢視到每一組資料樣本的測試結果。當同時執行多個類別的測試,Statistics 檢視不到相同的結果。所以這時可能就不能使用 JUnit 的 Configuration 來執行測試,要改為 Gradle 的 Configuration,並且在瀏覽器中來檢視測試報告。

截至目前為止,研究的結果大致都符合原先設定的需求條件,所以可以暫時完全先放下 Eclipse,開開心心地使用 Android Studio 上工囉!