ALM 開發人員的一日生活:為使用者劇本撰寫新程式碼
您是 Visual Studio Application Lifecycle Management (ALM) 的新使用者和 Team Foundation Server (TFS)? 您想知道您和小組如何從這些工具的最新版本取得最大優點建置您的應用程式?
然後需要幾分鐘的時間,逐步透過本教學課程的兩個章節並遵循一日 Peter 和 Julia 的生活—提供程式電視與相關服務的Fabrikam Fiber 虛構公司的兩位開發人員。 您將會看到示範如何使用 Visual Studio 和 TFS 簽出和更新程式碼,當您需要以中斷程式碼檢閱時暫止工作,簽入您的變更,以及執行其他工作。
到目前為止的故事
小組最近啟動 為生命週期管理應用程式 (ALM),使用 Visual Studio 和 Team Foundation Server。 他們設定他們的伺服器與用戶端,建立待處理項目,規劃反覆項目計劃,和執行其他必要計畫以開始開發自己的應用程式。
本章概觀
Peter 簡單地檢視它們的待處理項目並選取這些今天可以工作的工作。 對想要開發的程式碼,他寫了單元測試。 通常,他在 1 小時內執行測試數次,逐漸撰寫更詳細的測試,然後將其程式碼傳遞。 他經常與即將使用這些方法的同事討論其程式碼的介面。
注意事項 |
---|
在本主題中所討論的 [我的工作] 和 [程式碼涵蓋範圍功能] 只能在 Visual Studio Premium 和 Visual Studio Ultimate使用。 |
本主題內容
檢閱個人的待處理項目並準備工作以開始工作
建立第一個單元測試
為新的程式碼建立 Stub。
執行第一次單元測試
同意 API
紅色,綠色,重構…
程式碼涵蓋範圍
我們何時完成?
簽入變更
檢閱個人的待處理項目並準備工作以開始工作
在 [Team 總管]中,Peter 開啟 [我的工作] 頁面。 小組同意在目前期程,Peter 將為 Evaluate 發票狀態工作,在產品待處理項目的應用指派最優先的工作項目。 Peter 決定開始實作算術函式,最重要課題待處理項目的子工作。 他從 [可用的工作項目] 清單上拖曳此工作至 [正在進行中的工作項目&變更] 清單。
檢閱個人的待處理項目並準備工作以開始工作
在 [Team 總管] 中:
如果您尚未連接至要處理的 Team 專案,請連接到 Team 專案。
選擇 [首頁],然後選擇 [我的工作]。
在 [我的工作] 頁面,請將此 [可用的工作項目] 清單的工作加入至 [正在進行中的工作項目] 部分。
您也可以選取 [可用的工作項目] 清單中的工作然後選取 [開始]。
草稿加入工作專案
Peter 通常開發一系列的小步驟的程式碼。 每個步驟通常不超過 1 小時,可能花費約 10 分鐘。 在每一個步驟,他撰寫新的單元測試並變更他開發的程式碼,以便將新測試,除了測試之外識別他們已經被寫入。 有時候他會在變更程式碼之前撰寫新的測試,且在撰寫測試之前有時候會變更程式碼。 有時候他重構。 也就是只改善程式碼,而不加入新的測試。 他永遠不會變更成功的測試,除非他決定它未正確呈現需求。
在每個小步驟結束時,他會執行與程式碼的這個區域相關的所有的單元測試。 他不認為步驟完整,在每個測試都成功之前。
不過,他不會檢查程式碼至 Team Foundation Server,直到完成整個工作。
依照這種小步驟順序,Peter 寫下一個摘要的計劃。 他知道後面部分的確切詳細資料和命令可能會變更他的這些工作。 這是他的特定工作步驟的初始清單:
建立測試方法 Stub—這是方法的簽章。
滿足一種特定常見案例。
測試廣的範圍。 請確定程式碼正確回應大範圍的各種不同的值。
在負的例外狀況。 正常處理無效參數。
程式碼涵蓋範圍。 確定至少 80% 程式碼的被單元測試執行。
在他們的測試程式碼中,他的某些同事將註解寫入這個計劃。 其他人記住其計劃。 Peter 發現在Task工作項目的描述欄位中撰寫他的步驟清單很有用。 如果他應該必須暫時切換至更為緊急的工作,他知道哪裡能夠找到清單,當他可以回到時。
建立第一個單元測試
Peter 以建立單元測試開始。 他從單元測試開始,因為他想要撰寫使用這些新類別的範例程式碼。
這是類別庫的第一個單元測試,所以他建立新的單元測試專案。 他開啟了 [新增專案] 對話方塊並選取 [Visual C#]、 [測試] 然後 [單元測試專案]。
單元測試專案提供可供您撰寫範例的 C# 檔案。 在這個階段,他只想要說明他的新方法將如何被叫用:
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Fabrikam.Math.UnitTest
{
[TestClass]
public class UnitTest1
{
[TestMethod]
// Demonstrates how to call the method.
public void SignatureTest()
{
// Create an instance:
var math = new Fabrikam.Math.LocalMath();
// Get a value to calculate:
double input = 0.0;
// Call the method:
double actualResult = math.SquareRoot(input);
// Use the result:
Assert.AreEqual(0.0, actualResult);
}
}
}
他會在測試方法寫入此範例,因為,在撰寫自己的程式碼之後,他想要該範例的作用。
若要建立單元測試專案和方法
通常您會為被測試中的每個專案建立新的測試專案。 如果測試專案存在,您可以加入新測試方法和類別。
這個程序會使用 Visual Studio 單元測試架構,不過,您也可以使用其他提供者的框架。 提供您安裝適當的配接器,測試總管與其他框架一起使用同樣表現的很好。
建立測試專案,如果尚未存在。
- 在 [新增專案] 對話方塊中,選取一種語言 (例如 [Visual Basic]、 [Visual C++] 或 [Visual C#] )。 選取 [測試] 然後 [單元測試專案]。
將您的測試加入至提供的測試類別。 每個單元測試是一個方法。
每個單元測試的前置詞必須是 TestMethod 屬性,且單元測試方法中不應該有參數。 您可以使用想要的任何名稱用於單元測試方法:
[TestMethod] public void SignatureTest() {...}
<TestMethod()> Public Sub SignatureTest() ... End Sub
每個測試方法應該呼叫 Assert 類別的方法,以指出它是否通過或失敗。 一般來說,驗證作業的預期和實際的結果相等:
Assert.AreEqual(expectedResult, actualResult);
Assert.AreEqual(expectedResult, actualResult)
測試方法可以呼叫沒有 TestMethod 屬性的其他泛型方法。
您可以組織您的測試至一個以上的類別。 必須由 TestClass 屬性的前置詞命名每一個類別。
[TestClass] public class UnitTest1 { ... }
<TestClass()> Public Class UnitTest1 ... End Class
如需如何在C++中撰寫單元測試的詳細資訊,請參閱 使用適用於 C++ 的 Microsoft 單元測試架構撰寫適用於 C/C++ 的單元測試。
為新的程式碼建立 Stub。
接著,Peter 為他新的程式碼建立了類別庫專案。 現在有開發中程式碼的專案及單元測試的專案。 他從測試專案中將專案參考加入至開發中的程式碼。
在新的專案裡,他加入了新類別和會至少可讓測試成功建置之方法的最小版本。 最快的方法,是在測試中從調用(invocation)產生類別和方法 Stub 。
public double SquareRoot(double p)
{
throw new NotImplementedException();
}
產生類別和測試方法。
首先,建立要加入新類別的專案,除非已經存在的話。
產生 Managed 類別
將游標放在您要產生的類別,例如範例, LocalMath。 在捷徑功能表中,選取 [產生程式碼], [新增型別]。
在 [新增型別] 對話方塊中,將 [專案] 設至類別庫專案。 在此範例中,它是 Fabrikam.Math。
產生方法。
- 將游標放置在方法的呼叫,例如 SquareRoot。 在捷徑功能表中,選取 [產生程式碼], [方法 Stub]。
執行第一次單元測試
Peter 按下 CTRL+R , T以建置和執行測試。 測試結果會顯示紅色的失敗顯示,且測試隨即出現在 [失敗的測試] 底下的清單。
他對程式碼進行簡單的變更:
public double SquareRoot(double p)
{
return 0.0;
}
他重新執行測試,並且傳遞:
執行單元測試
在 [測試] 功能表上,選擇 [執行], [所有測試]。
-或-
如果測試總管開啟,請選取 [全部執行]。
-或-
在測試程式碼檔案放置游標然後按下 [CTRL+R、T,] 。
如果測試隨即出現在 [失敗的測試] 之下:
開啟測試,例如,按兩下名稱。
測試失敗點隨即顯示。
若要檢視測試的完整清單, 選取 [全部顯示]。 若要回到摘要,請選取 [首頁] 檢視。
若要檢視測試結果的詳細資料, 選取測試總管中的測試。
巡覽至測試的程式碼, 按兩下測試總管的測試或從捷徑功能表中選擇 [開啟測試] 。
偵錯測試, 開啟一個或多個測試的捷徑功能表,然後選取 [偵測選取的測試]。
在背景中執行測試,每次建置方案, 切換 [建置之後執行測試]。 之前失敗的測試會先執行。
授與介面
Peter 在 Lync 呼叫他的同事Julia,並共用他的畫面。 她將使用他的元件。 他顯示初始的範例。
Julia 辨識這個範例是良好的,不過註解了,「大量函式會通過測試」。
Peter 回覆「第一個測試只是確定函式的名稱和參數是正確的。 我們現在會擷取這個函式的主要需求的測試」。
同時他們撰寫下列測試:
[TestMethod]
public void QuickNonZero()
{
// Create an instance to test:
LocalMath math = new LocalMath();
// Create a test input and expected value:
var expectedResult = 4.0;
var inputValue = expectedResult * expectedResult;
// Run the method:
var actualResult = math.SquareRoot(inputValue);
// Validate the result:
var allowableError = expectedResult/1e6;
Assert.AreEqual(expectedResult, actualResult, allowableError,
"{0} is not within {1} of {2}", actualResult, allowableError, expectedResult);
}
提示
對於這個函式,Peter 使用測試優先開發,會先撰寫單元測試,然後撰寫符合測試中的功能的程式碼。在某些情況下,他發現這個作法並不實際,因此,他撰寫程式碼之後撰寫測試。不過,他認為撰寫單元測試非常重要—無論在寫程式碼之前或之後—因為它們會讓程式碼穩定。
紅色,綠色,重構…
Peter 遵循重複撰寫測試並確認週期移轉失敗,撰寫程式碼來讓測試成功,並且考慮重構—改善程式碼,而不會變更測試。
紅色
Peter 按下 CTRL+R, T,以執行他與 Julia 建立的新測試。 他撰寫測試之後,一律會執行它,以確定它失敗,在寫入通過的程式碼之前。 這是他學習到的經驗,在他忘記寫一些測試中的斷言之後。 請參閱失敗結果給他的的信賴等級,他讓它傳遞時,測試結果正確,需求可以被滿意。
另一個有用的作法是設定 [建置之後執行測試]。 這個選項在背景中執行測試,在您每次建置方案時,因此,您有一份程式碼測試狀態的執行報告。 Peter 是第一個懷疑它可能讓 Visual Studio 變慢回應,但發現不常發生。
綠色
Peter 第一次嘗試在其開發方法的程式碼:
public class LocalMath
{
public double SquareRoot(double x)
{
double estimate = x;
double previousEstimate = -x;
while (System.Math.Abs(estimate - previousEstimate) > estimate / 1000)
{
previousEstimate = estimate;
estimate = (estimate * estimate - x) / (2 * estimate);
}
return estimate;
}
Peter 重新執行測試,而且所有測試都成功:
重構
現在程式碼執行其主函式,Peter 查看程式碼尋找如何執行得更好,或如何在未來更容易作變更。 他了解他可以減少重複執行的計算次數:
public class LocalMath
{
public double SquareRoot(double x)
{
double estimate = x;
double previousEstimate = -x;
while (System.Math.Abs(estimate - previousEstimate) > estimate / 1000)
{
previousEstimate = estimate;
estimate = (estimate + x / estimate) / 2;
//was: estimate = (estimate * estimate - x) / (2 * estimate);
}
return estimate;
}
他確認測試仍然可以通過:
提示
當您開發程式碼時您所做的每項變更,應該是重構或擴充:
-
重構表示您不會變更測試,因為您不會增加任何新的功能。
-
擴充功能是加入測試並進行需要透過現有或新測試的程式碼變更。
如果您要針對已經變更的要求更新現有程式碼,也會刪除不再表示目前所要求之舊的測試。
避免變更已經通過的測試。相反地,請加入新的測試。只寫入表示真實所需的測試。
執行測試的詳細資料。
…和迴圈
Peter 繼續他的擴充功能系列和重構步驟,使用他的清單做為概略指南。 他不在每個擴充功能後一律執行重構步驟,且有時會連續執行一個以上的重構的步驟。 但他永遠執行單元測試,在程式碼進行每項變更之後。
有時候他加入的測試不需要程式碼的變更,不過加入的信賴等級測試它們的程式碼正確運作。 例如,他想要確定函式會在各種大範圍的輸入作用。 他撰寫更多測試,如這個:
[TestMethod]
public void SqRtValueRange()
{
LocalMath math = new LocalMath();
for (double expectedResult = 1e-8;
expectedResult < 1e+8;
expectedResult = expectedResult * 3.2)
{
VerifyOneRootValue(math, expectedResult);
}
}
private void VerifyOneRootValue(LocalMath math, double expectedResult)
{
double input = expectedResult * expectedResult;
double actualResult = math.SquareRoot(input);
Assert.AreEqual(expectedResult, actualResult, expectedResult / 1e6);
}
第一次執行時,這個測試成功:
判斷結果不是錯誤,他暫時引入了小型錯誤輸入他的測試來讓其失敗。 在失敗之後,他再次修復。
提示
請務必讓測試失敗在您成功通過之前。
例外狀況
Peter 現在撰寫例外輸入的測試:
[TestMethod]
public void RootTestNegativeInput()
{
LocalMath math = new LocalMath();
try
{
math.SquareRoot(-10.0);
}
catch (ArgumentOutOfRangeException)
{
return;
}
catch
{
Assert.Fail("Wrong exception on negative input");
return;
}
Assert.Fail("No exception on negative input");
}
這個測試將程式碼置於迴圈。 他必須使用測試總管的 [取消] 按鈕。 這會在 10 秒內終止程式碼。
Peter想要確定無限迴圈不會發生在組建伺服器上。 雖然伺服器完全執行時被認定強制逾時,這是很長的逾時,且可能產生重組延遲。 因此,他加入明確的時間逾時至此測試:
[TestMethod, Timeout(1000)]
public void RootTestNegativeInput()
{...
明確逾時使測試失敗。
然後 Peter 更新程式碼以處理這個例外狀況:
public double SquareRoot(double x)
{
if (x <= 0.0)
{
throw new ArgumentOutOfRangeException();
}
回復
新的測試成功,但是,有回復。 之前成功的測試現在失敗:
Peter 尋找和修正錯誤:
public double SquareRoot(double x)
{
if (x < 0.0) // not <=
{
throw new ArgumentOutOfRangeException();
}
在已修正後,所有測試都成功:
提示
判斷每一個測試通過,在對程式碼的每個變更之後。
程式碼涵蓋範圍
在他工作過程的時間間隔,最後在他簽入程式碼之前,Peter 取得了 [程式碼涵蓋範圍] 報表。 這會顯示多少程式碼已執行他套用的測試。
Peter 的小組以涵蓋範圍至少 80% 為目標。 他們放寬產生程式碼的要求,因為對這種類型的程式碼達到高的涵蓋範圍可能有困難。
好的涵蓋範圍不確保元件的完整功能已被測試,因此,它不能確保程式碼對輸入值的每一個範圍中良好運作。 不過,在[程式碼行涵蓋範圍]與[元件行為空間涵蓋範圍]之間有一種很接近的相互關聯。 因此,好的涵蓋範圍加強小組的自信,他們測試了他們應該作的大部分行為。
若要取得[程式碼涵蓋範圍] 報表,在 [測試] 功能表上,選取 [執行], [分析所有測試的程式碼涵蓋範圍]。 然後再次執行全部測試。
Peter 取得總涵蓋 86%。 當他展開在報表時的總數,顯示開發的程式碼涵蓋範圍有 100%。 因為重要分數是根據測試的程式碼,這非常令人滿意。 未涵蓋的部分實際是測試本身。 切換按鈕 [顯示程式碼涵蓋範圍著色] ,Peter 可以看到測試程式碼部分尚未執行。 不過,他決定這些部分涵蓋範圍並不重要,因為它們在測試程式碼,並且只使用在如果偵測到錯誤。
若要驗證特定測試到達程式碼的特定分支,使用在捷徑功能表中的, [執行] 命令,您可以設定 [顯示程式碼涵蓋範圍著色] 然後執行單一測試。
我們何時完成?
Peter 繼續以小步驟更新程式碼直到滿意:
所有可用的單元測試通過。
在使用非常大量的單元測試的專案,開發人員將它們全部執行是不切實際的。 相反地,專案作業的閘道簽入組建服務,任何自動化測試會針對每個已註冊的擱置集,在執行合併至原始碼結構樹之前。 如果執行失敗,簽入遭拒。 這可讓開發人員執行最小單元測試藉由他的使用電腦,然後繼續執行其他工作,而不用冒風險中斷建置。 如需詳細資訊,請參閱定義閘道簽入建置流程來驗證變更。
程式碼涵蓋範圍以符合小組的準則。 75% 是典型的專案需求。
他的單元測試可以模擬需要行為的各個層面,包括一般和例外輸入。
他的程式碼易於了解和擴充。
當這些準則實現,Peter 準備檢查他的程式碼至原始檔控制。
單元測試程式碼開發原則
Peter 套用下列準則,在開發程式碼時:
在開發期間,與程式碼一起開發單元測試,並經常執行它們。 單元測試表示元件的規格。
請勿變更單元測試,除非必要變更或測試是錯誤的。 逐漸加入新測試,擴充程式碼的功能。
針對測試中至少 75% 的程式碼被包含為目標。 請參閱程式碼涵蓋範圍結果間隔,在您簽入原始程式碼之前。
與程式碼一起簽入您的單元測試,因此他們會被依循序或一般伺服器組建執行。
可能的話,提供功能的每個部分,請先撰寫單元測試。 請這麼做,在您開發滿足其程式碼之前。
簽入變更
在查看他的變更之前,Peter 重複使用 Lync 與他的同事 Julia 分享螢幕,讓她可以非正式地且互動地檢閱他所建立的。 測試仍然是其討論焦點,因為 Julia 主要是對程式碼執行結果感興趣,而不是它的運作方式。 Julia 同意那些 Peter 撰寫符合其需求的。
Peter 將它們簽入所做的任何變更,包括測試和程式碼,並將它們與完成的工作關聯。 藉由使用 Team's CI Build 的建置流程,簽入排入小組的自動化 Team Build 系統來驗證這些變更。 這個建置流程協助小組透過建置降低其程式碼基底的錯誤,並測試在不同於其開發的乾淨的環境中電腦的每項變更小組進行。
在組建完成時,Peter 收到通知。 在建置結果視窗,他看到組建成功,而且所有測試都已通過。
簽入變更。
在功能表列上,選擇 [ 檢視], 小組總管。
在 [Team Explorer] 中,選擇 [首頁],然後選擇[我的工作]。
在 [我的工作] 頁面上,選擇 [簽入]。
檢閱 [暫存的變更] 網頁的內容,以確定:
所有相關的變更被列在 [包含的變更] 上
任何相關的工作項目被列在 [相關工作項目] 。
當他們查看已變更的檔案和資料夾的版本控制記錄時,請指定 [註解] 協助小組了解這些變更的目的。
選擇簽入。
連續整合程式碼
如需如何定義連續整合之建置流程的詳細資訊,請參閱定義建置流程以支援連續整合。 在您已經設定了這個建置流程之後,您可以選擇會收到有關 Team Build 的結果。
如需詳細資訊,請參閱執行、監視和管理組建。