使用 CI 進行自動化測試

Last updated on

2019 iT 邦幫忙鐵人賽 🔗文章補完計劃,從零開始建立自動化發佈的流水線 持續整合篇

想達成持續整合(Continuous Integration, CI)的目標,只有單純進行版本控制與建置,就太可惜了。CI 可視為是自動化管控軟體品質的第一道防線,在這個階段,可以整合自動化測試、程式碼掃描分析、資安掃描等動作。

接著,在本篇文章內章,會淺講單元測試的概念,並試著把單元測試與 CI 整合起來。

Eric:
  完成持續整合與訊息通知後,又向持續發佈的目標前進了一大步。

吉米:
  是啊,完成 CI server 的架設與訊息通告的設定後,就可以立刻確定提交到版控的程式是否能正常建置。

Eric:
  很好,但還不夠。我們下一步就是確保建置出來的軟體,可以正確運行功能。

吉米:
  我們在提供之前的測試,不就是為了確保軟體正確運行功能嗎?

Eric:
  這在軟體規模小的時候還可以,但是如果你手上的軟體己經持續發展六七年以上,還有辦法修改後的程式,每個功能都正常嗎?

吉米:
  確實,通常都是只測試這次修改的功能是否正常,其他功能不太會特別去測試。

Eric:
  對吧!所以這個時間,就要依賴自動測試機制,來檢查原有的功能是否正確運行。

吉米:
  你說的有道理,但我對自動化測試這部份沒有經驗。

Eric:
  沒關係,先跟你說自動化的觀念後,以後,你有機會會用到的。

自動化測試的第一步 - 單元測試

提到自動化測試,那就一定避不開單元測試。單元測試最主要的目的,就是驗證被測試的物件,商業邏輯的是否正確輸出入的結果是否符合預期

我們利用單元測試來快速測試、驗證,所開發的功能,是否能達到預期的結果。對於通過測試的功能,至少可以保證它的商業邏輯,在特定的測試的條件是正確無誤的。

單元測試的五大特性

而一個好的單元測試,它一定具有 快速獨立可重現性可自我驗證即時性 這五個特性。通常將這五個特性簡稱為 FIRST

  • Fast 快速

    單元測試可以在短時間取得測試結果。

  • Independent/Isolated 獨立

    單元測試時,不需依賴其他的物件或功能,才能進行測試。

  • Repeatable 可重現的

    不管在何時何地,測試的結果應該都是相同的。

  • Self-Validating 可自我驗證

    不管單元測驗通過與否,應該可以從測試工具產生的測試報告,明白的得知測試結果與失敗原因。

  • Timely 即時

    可以當下就知道測試結果。

單元測試的基本功

對於沒有撰寫過單元測試的開發者,往往不知道要從那邊開始下手。或者是手上的程式歷經 N 個人的手,內容過於複雜,無法下撰寫單元測試。

事實上,單元測試的運用與觀念,非常重視 物件導向的特性SOLID 原則。所以,假若不常用或不熟悉物件導向的開發者,初期不容易切入單元測試的世界。

簡單提一下 物件導向的特性SOLID 原則 ,若要進一步了解,網路上也有許多參考資料,就不詳細說明。

物件導向特性

  • 封裝: 將相關的功能、參數、全部包成同一個物件。
  • 繼承: 繼承父物件的子物件,具有父物件的特定功能的同時,可以增加父類別所沒有的功能。
  • 多型: 使用相同的抽象介面,操作同介面不同實作的物件。
  • 抽象: 物件間共有的特性或功能,但不一定具有實作功能。

SOLID 原則

  • 單一職責原則(Single responsibility principle, SRP)

    每個物件,不管是類別、函數,負責的功能,都應該只做一件事。也因為一次只做一件事,所以它的目的是非常明確的。

    相當來說,目的越明確,範圍越小的功能,其單元測試就越容易撰寫。

  • 開放封閉原則(Open-Close principle, OCP)

    當功能變改時,該藉由增加新的程式碼來擴充系統的功能。避免修改原本的程式碼來擴充系統的功能。

    修改原本正常運行的程式碼,你很難確保這次的變更,是否如預期般,不會影響到其他相關的功能。

    就個人經驗而言,有所影響是正常的,沒影響到是運氣好,但通常會對維護上,帶來額外的負擔。

  • 里氏替換原則(Liskov substitution principle, LSP)

    簡單來說,只要是繼承父類別的物件。在程式中,只要出現父類別物件的部份,都可以用子類別替換。

  • 接口隔離原則(Interface segregation principle, ISP)

    針對不同需求的用戶,開放其對應需求的介面,提供使用。可避免不相關的需求介面異動,造成被強迫一同面對異動的情況。

    簡單來說,對不同的用戶,開放不同的功能。再用通俗一點的說法,就是見人說人話、見鬼說鬼話。

  • 依賴反轉原則(Dependency inversion principle, DIP)

    類別中,不應該直接使用另一個具有實作類別,而是使用抽象的介面,去承接繼承該介面的實作類別。它的目標就是解除物件與物件間,兩者的直接相依關係。

單元測試的用途

當軟體規模大到一個程度後,後面的需求變動,往往有很大的機率出現改 A 壞 B的情況。在單元測試保護的情況下,程式改壞了,測試就會直接亮紅燈,以確保程式功能的正確性。

或是軟體需要重構的情況下,確保軟體不會因為程式碼的整理,而造成功能的損壞。

再者時,軟體己具有一定規模時,快速驗證新增功能是否正常,而不需要每次都從重跑流程,造成時間與效率的浪費。

所以 單元測試,真正發揮它的 POWER 的時間點,在於軟體後續長期維護、需求變動、重構時,才能真正的明白為什麼需要單元測試。

順帶一提,在單元測試時,不需要過份追求程式覆蓋率測試案例。過度追求程式覆蓋率或測試案例,可能會造成無意義的測試案例、或是測試案例維護成本過高的情況。在無法確定那些功能需要測試的情前提下,可先從使用頻率最高曾經出錯的功能下手。

設定 CI 運行規則

Eric:
  當我們寫完了自動測試,當然也會希望 CI server 在建置時,順便幫我們確認提交變更後的程式,是否可以通過測試。

吉米:
  嗯嗯,所以我們接著要去 Azure DevOps、Travis CI 這幾個 CI Server 中,設定執行自動測試的動作吧。

Eric:
  沒錯。那我們就接著進行 CI Server 的設定吧。

在開始之前,先建立一個名稱為 Calculate.Tests 的.net core unit test 專案,特別注意,使用的測試工具是 MSTest

[TestMethod]
public void Plus_1_plus_1_equ_2()
{
    CalculateTools.Calculate calc = new CalculateTools.Calculate();
    int expected= 2;

    int actual = calc.Plus(1, 1);

    Assert.AreEqual(expected, actual);
}

Travis CI

▶ 若測試框架為 MSTest,由於 MSTest 為 dotnet 的內含工具,只需在 .travis.yml 增加一行指令,就可以進行測試了。

script:
   - dotnet test Calculate.Tests/Calculate.Tests.csproj

從 Travis CI 的 log 中,就可以看到 Travis CI 真的有執行測試動作。

Travis CI 執行結果
Travis CI 執行結果

▶ 假若使用 NUnit 或 xUnit 等測試框架,就需要額外設定,讓 Travis CI 知道有使用外部套件。

下面是 Travis CI 官方說明文件的 yml 範例。

language: csharp
solution: solution-name.sln
install:
  - nuget restore solution-name.sln
  - nuget install xunit.runners -Version 1.9.2 -OutputDirectory testrunner
script:
  - xbuild /p:Configuration=Release solution-name.sln
  - mono ./testrunner/xunit.runners.1.9.2/tools/xunit.console.clr4.exe ./MyProject.Tests/bin/Release/MyProject.Tests.dll

Azure DevOps

對於自家的 .NET 專案,Azure DevOps 的支援性就更高了。

無需額外設定,Azure Pipleline 在建置時,發現有測試專案,就會自動進行測試。

Azure Pipleline
Azure Pipleline

除了單元測試以外,還想加入其他的測試計劃,可以選擇 Test Plans。這部份就不多加著墨,有興趣可以自行摸索。

Azure Test Plan
Azure Test Plan
吉米:
  當 CI 整合測試後,感覺又多了一層保障。

Eric:
  是啊,只要通過 CI ,可以確實每一個提交者的程式,都是通過測試的。

吉米:
  也就是,只有要人沒通過測試,就把變更後的程式提交上來,立馬就會被抓包了。

Eric:
  哈哈哈,要這樣說也可以。

延伸閱讀

▶ 站內文章

▶ 單元測試

▶ Travis CI


Series
從零開始建立自動化發佈的流水線