單元測試

單元測試

1 前言

單元測試 (Unit Test) 是最基本、最細節的測試,它是一種白箱 (White Box)測試,主要由原開發人員負責測試程式碼每一段邏輯是否合乎預期,由於目標是要徹底測試,想當然耳這些工作花費的人力差不多等同於撰寫程式的人力,對於有時程、人力限制的軟體專案,單元測試不是很容易推行,但是推行這樣的制度有什麼好處?若系統程式碼有著完整的單元測試個案,進行後續的整合是不是問題會比較少?若需求變動導致程式碼修改,透過這些單元測試個案進行廻歸測試,能協助我們找出相關影響程式碼部份?不過要推行單元測試一個大前提,必須簡化單元測試開發流程,降低人力花費,儘可能自動化,這些目標Visual Studio 2010已經幫我們達成。

2 相關工具介紹

  • Test Tools工具列

位置於Visual Stuido Ultimate Menu -> Toolbars -> Test Tools,單元測試相關工具捷徑列。

clip_image002

圖 Test Tools工具列

  • Test View視窗

自動掃描目前.sln所包含的測試個案清單,預設以英文字母順序排列,透過它您單選或多選測試個案執行。

clip_image004

圖 Test View視窗

  • Test Results視窗

顯示測試個案執行結果以及相關錯誤訊息。

clip_image006

圖 Test Results視窗

  • Code Coverage Results視窗

顯示測試個案執行後程式碼涵蓋度結果。

clip_image008

圖 Code Coverage Results

  • Test Impact View視窗

顯示本次程式碼異動所影響到的測試清單。

clip_image010

圖 Test Impact View視窗

  • Test Project

用於承載測試個案的專屬專案檔,單元測試程式碼只能存放於此,不允許混雜在其他專案裡。

3 從原始碼產生測試個案步驟

時機:當開發人員完成了程式實作,並且想要依據目前程式碼結構產生單元測試程式碼樣版時。

說明:Visual Stuido 2010會依據程式碼裡的類別(Class)與方法(Method)結構,讓開發人員挑選那些方法(Method)欲產生單元測試程式碼樣版。

步驟:

  • 請將” 01 Generate Unit Test \ Copy of Begin”目錄內容複製並覆蓋”Begin”目錄。
  • 請開啟” 01 Generate Unit Test \ Begin”目錄之DiscussionApplication.sln
  • 請至Solution Explorer 開啓 LogicLibrary / AuthenticationUtility.cs。
  • 在LogOn() Method按右鍵選”Create Unit Tests”。
  • 此時會要求您選擇那些方法 (Method) 要產生單元測試樣版,”Output project”值維持原則”Create a new Visual C# test project”,然後按[OK]鈕,以產生測試專案 (Test Project),以承載單元測試程式碼樣版。

clip_image012

圖 選擇那些方法 (Method) 要產生單元測試樣版

  • 輸入測試專案名稱,如”DiscussionTestProject”,按[Create]鈕。
  • 完成後單元測試程式碼樣版會在DiscussionTest Project / AuthenticationUtilityTest.cs,請開啓它,移至LogOnTest() 方法(Method)。

注意!Visual Studio 2010會自動偵測類別(Class) 與方法(Method) 的中介資料 (Metadata),以決定有那些單元測試可供執行,類別(Class)上方會有[TestClass()]、方法(Method)上方會有[TestMethod()]。

  • 在程式碼樣版裡,您可以調整輸出入相關參數,在下方有一行Assert.Inconclusive(),它用於標註此測試方法尚未實作,當我們修改好了測試邏輯,記得刪除Assert.Inconclusive()此行程式碼,修改後程式碼如下:

/// <summary>

///A test for LogOn

///</summary>

[TestMethod()]

public void LogOnTest()

{

AuthenticationUtility target = new AuthenticationUtility();

string userName = "Euro";

string password = "Euro";

bool expected = true;

bool actual;

actual = target.LogOn(userName, password);

Assert.AreEqual(expected, actual);

}

注意!最後一行程式碼使用到Assert類別,透過這個類別用於比對單元測試標的的產出是否符合預期,若不符合則會拋出例外(Exception) ,使此單元測試個案變成測試失敗。

  • 從Test Tools工具列點選[Test View]鈕,若測試方法沒有出現,請按[Refresh]鈕,選擇”LogOnTest”後,按[Run Selection]鈕,以進行單元測試。

clip_image004[1]

圖 Test View

  • 若執行成功會在Test Results視窗顯示”Passed”。

clip_image006[1]

圖 Test Results視窗

4 測試錯誤處理步驟

時機:除了正常程序需要單元測試,異常處理也是需要單元測試。

說明:自行新增測試方法,並利用宣告中介資料(Metadata)方式來進行異常處理單元測試。

步驟:

  • 請將” 02 Handle Exception \ Copy of Begin”目錄內容複製並覆蓋”Begin”目錄。
  • 請開啟” 02 Handle Exception \ Begin”目錄之DiscussionApplication.sln
  • 請至Solution Explorer開啓 DiscussionTestProject / AutheticationUtilityTest.cs
  • 複製LogOnTest() 方法內容並更名為 LogOnInvalidTest()方法,內容如下:

/// <summary>

///A test for LogOn

///</summary>

[ExpectedException(typeof(UserNameOrPasswordRequiredException))]

[TestMethod()]

public void LogOnInvalidTest()

{

AuthenticationUtility target = new AuthenticationUtility();

string userName = null;

string password = "Euro";

bool expected = true;

bool actual;

actual = target.LogOn(userName, password);

Assert.AreEqual(expected, actual);

}

注意!LogOnInvalidTest()方法上方宣告ExpectedException(),參數會是預期的Exception之類別(Type)。

  • 回到Test View視窗,執行LogOnInvalidTest(),結果如下:

clip_image014

圖 LogOnInvalidTest()執行結果

5 Data-Driven 單元測試(Unit Test)步驟

時機:當測試個案流程比較複雜,需要不同資料組合進行測試時。

說明:可以運用資料庫、XML檔、CSV檔儲存不同的輸出入資料組合,Visual Studio會讀取儲存庫資料,以廻圈方式執行同一個測試。

步驟:

  • 請將”03 Data-driven Unit Test \ Copy of Begin”目錄內容複製並覆蓋”Begin”目錄。
  • 請開啟”03 Data-driven Unit Test \ Begin”目錄之DiscussionApplication.sln
  • 透過Server Explorer,開啓 DiscussionDatabase / Tables / UnitTestLogOnData,按右鍵選”Show Table Data”,筆者在此資料表事先建置所有輸出入資料組合

clip_image016

圖 UnitTestLogOnData

注意!運用Data-driven測試方法,您必須建立一個Table對應一個被測試方法(Method),如本例LogOn()方法,結構包含所有輸入參數(UserNameValue、PasswordValue)以及輸出預期結果(ExpectedValue)。可以預期的是會有一堆Table,筆者建議可以額外建置一個資料庫用於儲存這些測試資料。

  • 請至Solution Explorer開啓 DiscussionTestProject / AutheticationUtilityTest.cs,在下方新增下列方法:

[TestMethod()]

public void MultiLogOnTest()

{

AuthenticationUtility target = new AuthenticationUtility();

string userName = (string)TestContext.DataRow["UserNameValue"];

string password = (string)TestContext.DataRow["PasswordValue"];

bool expected = (bool)TestContext.DataRow["ExpectedValue"];

bool actual;

actual = target.LogOn(userName, password);

Assert.AreEqual(expected, actual);

}

注意!透過TestContext物件,您可以讀取儲存庫內容,DataRow[]陣列指定欄位名稱。

  • 在Test View裡選擇”MultiLogOnTest”,按[F4]鈕,在屬性(Properties) 視窗,點選”Data Connection String”右方按鈕。

clip_image018

圖 點選Data Connection String屬性

  • 選擇Database,按[Next]鈕。

clip_image020

圖 選擇Database

  • 選擇資料庫連線,按[Next]鈕。

clip_image022

圖 選擇資料庫連線

  • 選擇欲讀取測試資料之資料表(Table)。

clip_image024

圖 選擇欲讀取測試資料之資料表(Table)

  • 完成後會在MultiLogOn()方法上方新增下列中介資料(Metadata):

[DataSource("System.Data.SqlClient", "Data Source=(local);Initial Catalog=DiscussionDatabase;Integrated Security=True", "UnitTestLogOnData", DataAccessMethod.Sequential), TestMethod()]

  • 在Test View選擇MultiLogOnTest執行單元測試,在Test Results視窗選擇測試結果按右鍵選”View Test Results Details”,可得知此測試所含每次實際執行細節。

clip_image026

圖 單元測試執行結果細節

6 檢測Code Coverage步驟

時機:當單元測試已實作一些出來,我們想要了解這些測試所包含邏輯是否考慮周詳。

說明:Code Coverage意指當本次測試執行時,所有執行的測試對應測試標的的程式碼涵蓋程度,Visual Studio除了提供數據以外,並且以顏色區別那些路徑被測試過,那些則無。

注意!Visual Studio 2010的Code Coverage設定流程與Visual Studio 2008非常不一樣,設定檔由localtestrun.testrunconfig改為local.testsettings,設定畫面也完全不一樣。

注意!每一次執行測試時,不管您是選擇一個測試或是多個測試,皆會產生一個測試報告,檔名格式為”{帳號}_{電腦名稱} {測試日期時間}.trx”,會存於專案目錄\TestResults。

步驟:

  • 請將”04 Examine Code Coverage \ Copy of Begin”目錄內容複製並覆蓋”Begin”目錄。
  • 請開啟”04 Examine Code Coverage \ Begin”目錄之DiscussionApplication.sln
  • 在Solution Explorer裡,對local.testsettings 滑鼠點擊兩下,開啓Test Settings畫面,並切換至”Data and Diagnostics”頁,將右下方表格之”Code Coverage”項目勾選起來,再按[Configure]鈕。

clip_image028

圖 Test Settings畫面

  • 將測試標的LogicLibrary.dll檔勾選起來,按[OK]鈕。

clip_image030

圖 勾選測試標的

  • 回到Test Settings畫面,按[Apply]鈕,再按[Close]鈕,以儲存設定值。
  • 在Test View視窗全選所有測試個案,執行單元測試。
  • 在Test Results視窗裡所有測試個案全數通過,任選一個測試,按右鍵選”Code Coverage Results”,即可檢視本次測試的Code Coverage Results。

clip_image031

圖 Code Coverage Results

注意!理論上測試個案的涵蓋度(Code Coverage)應該要到100%,但在實務上很難達成,我們會撰寫很多異常處理程式碼,但是這一部份很難測試完整,比如要模擬記憶體耗盡的測試個案便很難設計,但是我們的程式碼仍需這種情況。

7 界定測試衝擊程度(Test Impact)步驟

時機:當單元測試已經實作出來,此時我們需要變更程式碼,想要了解那些單元測試會被影響到。

說明:Viusal Studio會以上一次測試報告為基準,比對目前程式碼有那些方法(Method)異動,列出有測試到被異動方法(Method)之單元測試。

注意!測試衝擊程度(Test Imapct)功能是Visual Studio 2010新增功能。

步驟:

  • 請將”05 Identify Test Impact \ Copy of Begin”目錄內容複製並覆蓋”Begin”目錄。
  • 請開啟”05 Identify Test Impact \ Begin”目錄之DiscussionApplication.sln
  • 在Solution Explorer裡,對local.testsettings 滑鼠點擊兩下,開啓Test Settings畫面,並切換至”Data and Diagnostics”頁,將右下方表格之”Test Impact”項目勾選起來,按[Apply]鈕,再按[Close]鈕儲存設定值。

clip_image033

圖 Test Settings畫面

  • 在Test View視窗選擇所有單元測試並執行之,以建立測試衝擊(Test Impact)之基準。
  • 開啟AuthenticationUtility.cs,在LogOn()方法增加一段檢查邏輯,如下:

public bool LogOn(string userName, string password)

{

if (String.IsNullOrEmpty(userName) || String.IsNullOrEmpty(password))

{

throw new UserNameOrPasswordRequiredException();

}

if (password.Length < 4)

{

throw new UserNameOrPasswordRequiredException();

}

if (userName.Equals("Euro") && password.Equals("Euro"))

return true;

else

return false;

}

  • 按[F6]鈕將程式碼進行編譯,在Test Tools工具列上,按[Test Impact View]鈕以顯示Test Impact View視窗。
  • 在Test Impact View之上方區域會顯示本次異動會影響到三個單元測試,下方區域顯示被異動的方法(Method)。

clip_image035

圖 Test Impact View視窗

  • 回到AuthenticationUtility.cs之LogOn()方法,對它按右鍵選”Show Calling Tests”,會顯示此方法所對應的單元測試。

clip_image037

圖 Show Calling Tests

  • 在Test Impact View裡,按[Run All Impacted Tests]鈕以執行相關單元測試,執行完成的測試報告會當成新的基準,所以Test Impact View裡的資訊就會清除。

8 結語

在Visual Studio 2010之單元測試並沒有太大的變化,主要是Code Coverage設定畫面有變,新增測試衝擊(Test Impact View)視窗。