單元測試
1 前言
單元測試 (Unit Test) 是最基本、最細節的測試,它是一種白箱 (White Box)測試,主要由原開發人員負責測試程式碼每一段邏輯是否合乎預期,由於目標是要徹底測試,想當然耳這些工作花費的人力差不多等同於撰寫程式的人力,對於有時程、人力限制的軟體專案,單元測試不是很容易推行,但是推行這樣的制度有什麼好處?若系統程式碼有著完整的單元測試個案,進行後續的整合是不是問題會比較少?若需求變動導致程式碼修改,透過這些單元測試個案進行廻歸測試,能協助我們找出相關影響程式碼部份?不過要推行單元測試一個大前提,必須簡化單元測試開發流程,降低人力花費,儘可能自動化,這些目標Visual Studio 2010已經幫我們達成。
2 相關工具介紹
- Test Tools工具列
位置於Visual Stuido Ultimate Menu -> Toolbars -> Test Tools,單元測試相關工具捷徑列。
圖 Test Tools工具列
- Test View視窗
自動掃描目前.sln所包含的測試個案清單,預設以英文字母順序排列,透過它您單選或多選測試個案執行。
圖 Test View視窗
- Test Results視窗
顯示測試個案執行結果以及相關錯誤訊息。
圖 Test Results視窗
- Code Coverage Results視窗
顯示測試個案執行後程式碼涵蓋度結果。
圖 Code Coverage Results
- Test Impact View視窗
顯示本次程式碼異動所影響到的測試清單。
圖 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),以承載單元測試程式碼樣版。
圖 選擇那些方法 (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]鈕,以進行單元測試。
圖 Test View
- 若執行成功會在Test Results視窗顯示”Passed”。
圖 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(),結果如下:
圖 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”,筆者在此資料表事先建置所有輸出入資料組合
圖 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”右方按鈕。
圖 點選Data Connection String屬性
- 選擇Database,按[Next]鈕。
圖 選擇Database
- 選擇資料庫連線,按[Next]鈕。
圖 選擇資料庫連線
- 選擇欲讀取測試資料之資料表(Table)。
圖 選擇欲讀取測試資料之資料表(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”,可得知此測試所含每次實際執行細節。
圖 單元測試執行結果細節
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]鈕。
圖 Test Settings畫面
- 將測試標的LogicLibrary.dll檔勾選起來,按[OK]鈕。
圖 勾選測試標的
- 回到Test Settings畫面,按[Apply]鈕,再按[Close]鈕,以儲存設定值。
- 在Test View視窗全選所有測試個案,執行單元測試。
- 在Test Results視窗裡所有測試個案全數通過,任選一個測試,按右鍵選”Code Coverage Results”,即可檢視本次測試的Code Coverage Results。
圖 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]鈕儲存設定值。
圖 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)。
圖 Test Impact View視窗
- 回到AuthenticationUtility.cs之LogOn()方法,對它按右鍵選”Show Calling Tests”,會顯示此方法所對應的單元測試。
圖 Show Calling Tests
- 在Test Impact View裡,按[Run All Impacted Tests]鈕以執行相關單元測試,執行完成的測試報告會當成新的基準,所以Test Impact View裡的資訊就會清除。
8 結語
在Visual Studio 2010之單元測試並沒有太大的變化,主要是Code Coverage設定畫面有變,新增測試衝擊(Test Impact View)視窗。