Devops 開發人員的一生中的一天:為用戶劇本撰寫新程序代碼

Azure DevOps Services | Azure DevOps Server 2022 - Azure DevOps Server 2019

Visual Studio 2019 |Visual Studio 2022

本教學課程將逐步引導您和小組如何從最新版本的 Team Foundation 版本控制 (TFVC) 和 Visual Studio 取得最大效益,以建置您的應用程式。 本教學課程提供如何使用 Visual Studio 和 TFVC 來取出和更新程式代碼、中斷時暫停工作、要求程式代碼檢閱、簽入變更,以及執行其他工作的範例。

當小組採用 Visual Studio 和 TFVC 來管理其程式代碼時,他們會設定其伺服器和用戶端電腦、建立待辦專案、規劃反覆專案,以及完成開始開發其應用程式所需的其他規劃。

開發人員會檢閱其待辦專案,以選取要處理的工作。 他們會撰寫其計劃開發之程式代碼的單元測試。 一般而言,他們會在一小時內執行測試數次,逐漸撰寫更詳細的測試,然後撰寫讓測試通過的程序代碼。 開發人員通常會與將使用其撰寫方法的同事討論其程式代碼介面。

Visual Studio My WorkCode Review 工具可協助開發人員管理其工作,並與同事共同作業。

注意

Visual Studio My WorkCode Review 功能適用於下列版本:

  • Visual Studio 2022:Visual Studio Community、Visual Studio Professional 和 Visual Studio Enterprise
  • Visual Studio 2019:Visual Studio Professional 和 Visual Studio Enterprise

檢閱工作項目並準備開始工作

小組已同意,在目前的短期衝刺期間,您將處理 產品待辦專案中的最高優先順序專案評估發票狀態。 您決定從實作數學函式開始,這是最優先待辦專案之子工作。

在 Visual Studio Team Explorer[我的工作] 頁面上,您將這項工作從 [可用的工作專案] 清單拖曳到 [進行中工作] 清單中。

檢閱待辦項目並準備工作以開始工作

[我的工作] 頁面的螢幕快照。

  1. Team Explorer 中,如果您尚未連線到您想要使用的專案,請 連線至專案

  2. 從 [首頁] 頁面中,選取 [我的工作]。

  3. 在 [ 我的工作] 頁面上,將工作從 [ 可用的工作專案 ] 清單拖曳至 [ 進行中工作 ] 區段。

    您也可以在 [可用的工作專案 ] 列表中選取工作,然後選取 [ 開始]。

草稿累加式工作計劃

您會在一系列小步驟中開發程序代碼。 每個步驟通常需要不超過一小時,而且可能需要 10 分鐘的時間。 在每個步驟中,您撰寫新的單元測試,並變更您正在開發的程式代碼,使其除了已撰寫的測試之外,也通過新的測試。 有時候您在變更程式碼之前先撰寫新的測試,有時在撰寫測試之前先變更程序代碼。 有時候您會重構。 也就是說,您只要改善程序代碼,而不需新增測試。 除非您決定測試未正確代表需求,否則您永遠不會變更通過的測試。

在每個小步驟結束時,您會執行與此程式代碼區域相關的所有單元測試。 在每次測試通過之前,您不會考慮步驟完成。

在完成整個工作之前,您不會將程式代碼簽入 Azure DevOps Server。

您可以針對這個小步驟順序寫下粗略的計劃。 您知道稍後的確切詳細數據和順序可能會在您工作時變更。 以下是此特定工作的初始步驟清單:

  1. 建立測試方法存根,也就是方法的簽章。
  2. 滿足一個特定的典型案例。
  3. 測試廣泛的範圍。 請確定程式代碼會正確回應大量的值。
  4. 負數的例外狀況。 以不正確的參數正常處理。
  5. 程式代碼涵蓋範圍。 請確定單元測試會執行至少 80% 的程式代碼。

有些開發人員會在測試程序代碼的批註中撰寫這類計劃。 其他人只是記住他們的計劃。 在 [工作工作] 專案的 [描述] 字段中撰寫步驟列表很有用。 如果您必須暫時切換到更緊急的工作,當您能夠返回清單時,您會知道在何處找到清單。

建立第一個單元測試

從建立單元測試開始。 從單元測試開始,因為您想要撰寫使用新類別的程式代碼範例。

這是您要測試之類別庫的第一個單元測試,因此您可以建立新的單元測試專案。

  1. 選取 [檔案] > [新增專案]。
  2. 在 [建立新專案] 對話框中,選取 [所有語言] 旁的箭號,然後選取 [C#],選取 [所有專案類型] 旁的箭號,然後選擇 [測試],然後選取 [MSTest 測試專案]。
  3. 選取 [下一步],然後選取 [建立]

在 [建立新專案] 對話框中選取 [單元測試] 的螢幕快照。

在程式代碼編輯器中,將 UnitTest1.cs 的內容取代為下列程序代碼。 在這個階段,您只想說明如何叫用其中一個新方法:

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 單元測試架構,但您也可以使用來自其他提供者的架構。 測試總管與其他架構搭配運作同樣良好,前提是您安裝適當的配接器。

  1. 使用上述步驟建立測試專案。 您可以選擇 C#F#Visual Basic語言。

  2. 將測試新增至所提供的測試類別。 每個單元測試都是一種方法。

    • 每個單元測試都必須加上 屬性的 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++ 的單元測試。

建立新程式代碼的存根

接下來,為您的新程式代碼建立類別庫專案。 現在有開發中程式代碼的專案,以及單元測試的專案。 將測試項目的項目參考新增至開發中的程式碼。

具有 Test 和 Class 專案的 方案總管 螢幕快照。

在新專案中,您會新增 類別和最少版本的方法,至少允許測試成功建置。 若要這麼做,最快的方式是從測試中的調用產生類別和方法存根。

public double SquareRoot(double p)
{
    throw new NotImplementedException();
}

從測試產生類別和方法

首先,請建立您要在其中新增類別的專案,除非它已經存在。

產生類別

  1. 將游標放在您想要產生的類別範例上, LocalMath例如 ,然後選取 [ 快速動作] 和 [重構]。
  2. 在快捷方式功能表上,選擇 [ 產生新類型]。
  3. 在 [產生類型] 對話框中,將 [專案] 設定為類別庫專案。 在此範例中 ,它是 Fabrikam.Math

產生方法

  1. 將游標放在方法的呼叫上,例如 , SquareRoot然後選取 [ 快速動作] 和 [重構]。
  2. 在快捷方式功能表上,選擇 [ 產生方法 'SquareRoot']。

執行第一個測試

建置並執行測試。 測試結果會顯示紅色的失敗指標,而測試會出現在 [失敗的測試] 清單中

[測試總管] 的螢幕快照,其中顯示一個失敗的測試。

對程式代碼進行簡單的變更:

public double SquareRoot(double p)
{
    return 0.0;
}

再次執行測試,並通過。

單元測試總管的螢幕快照,其中含有一個通過的測試。

執行單元測試

若要執行單元測試:

  • 選取 [ 測試>執行所有測試]
  • 或者,如果 [測試總管] 已開啟,請選擇 [執行] 或 [在檢視中執行所有測試]。

[測試總管] 的螢幕快照,其中顯示 [全部執行] 按鈕。

如果測試出現在 [失敗的測試] 底下,請按兩下名稱開啟測試。 測試失敗的點會顯示在程式碼編輯器中。

  • 若要查看完整的測試清單,請選擇 [ 全部顯示]。

  • 若要查看測試結果的詳細數據,請在 [測試總管] 中選取測試。

  • 若要流覽至測試的程式代碼,請在 [測試總管] 中按兩下測試,或在快捷方式功能表上選擇 [開啟測試]。

  • 若要對測試進行偵錯,請開啟一或多個測試的快捷方式功能表,然後選擇 [ 偵錯]。

  • 若要在建置方案時在背景中執行測試,請選取 設定 圖示旁的箭號,然後選取 [置後執行測試]。 先前失敗的測試會先執行。

同意介面

您可以與將透過共享畫面來使用元件的同事共同作業。 同事可能會評論許多函式會通過先前的測試。 說明此測試只是為了確保函式的名稱和參數正確無誤,現在您可以撰寫可擷取此函式主要需求的測試。

您可以與同事共同作業以撰寫下列測試:

[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);
}

提示

針對此函式,您會使用 測試第一個開發,在其中先撰寫功能的單元測試,然後撰寫符合測試的程序代碼。 在其他情況下,這種做法並不現實,因此您會在撰寫程式代碼之後撰寫測試。 但是撰寫單元測試非常重要,無論是在程式碼之前還是之後,因為它們會讓程式代碼保持穩定。

紅色、綠色、重構...

遵循迴圈,您重複撰寫測試並確認其失敗、撰寫程式代碼以讓測試通過,然後考慮重構,也就是在不變更測試的情況下改善程序代碼。

紅色

執行所有測試,包括您建立的新測試。 撰寫任何測試之後,請一律執行它,以確保它失敗,再撰寫程式代碼使其通過。 例如,如果您忘記在撰寫的某些測試中放置判斷提示,看到 失敗 結果可讓您確信當您通過時,測試結果正確表示已滿足需求。

另一個有用的做法是設定 建置後執行測試。 此選項會在每次建置方案時於背景執行測試,以便您持續回報程式碼的測試狀態。 您可能擔心這種做法可能會讓 Visual Studio 回應變慢,但這種情況很少發生。

測試總管的螢幕快照,其中含有一個失敗的測試。

綠色

在您要開發之方法的程式代碼中撰寫您的第一次嘗試:

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;
    }

再次執行測試,所有測試都會通過。

單元測試總管的螢幕快照,其中包含兩個通過的測試。

重構

現在程式代碼會執行其主要函式,請查看程式代碼,以尋找讓程式代碼執行得更好,或讓您在未來更容易變更。 您可以減少循環中執行的計算數目:

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;
    }

確認測試仍然通過。

提示

  • 您在開發程式代碼時所做的每一項變更都應該是重構或延伸模組:

    • 重構表示您不會變更測試,因為您未新增新功能。
    • 延伸模組表示新增測試和進行程式碼變更,以傳遞現有和新的測試。
  • 如果您要將現有的程式代碼更新為已變更的需求,您也會刪除不再代表目前需求的舊測試。

  • 避免變更已經通過的測試。 而改為加入新的測試。 只撰寫代表實際需求的測試。

  • 在每次變更之後執行測試。

...和重複

使用小步驟清單作為粗略指南,繼續您的一系列擴充和重構步驟。 您不一定會在每個擴充功能之後執行重構步驟,而且您有時會連續執行多個重構步驟。 但每次變更程式代碼之後,您一律會執行單元測試。

有時候,您會新增不需要變更程式碼的測試,但這會增加您對程式代碼正常運作的信心。 例如,您想要確定函式可在廣泛的輸入範圍內運作。 您可以撰寫更多測試,例如此測試:

[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);
}

此測試會在第一次執行時通過。

[測試總管] 的螢幕快照,其中包含三個通過的測試。

為了確定此結果不是錯誤,您可以暫時在測試中引入一個小錯誤,使其失敗。 看到失敗之後,您可以再次修正。

提示

在通過測試之前,請務必讓測試失敗。

例外狀況

現在,繼續撰寫特殊輸入的測試:

[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 秒內終止程序代碼。

您要確定建置伺服器上無法發生無休止的迴圈。 雖然伺服器會在完整執行時強加逾時,但這是很長的逾時,而且會造成大量延遲。 因此,您可以將明確的逾時新增至此測試:

[TestMethod, Timeout(1000)]
public void RootTestNegativeInput()
{...

明確逾時會使測試失敗。

更新程式代碼以處理此例外狀況:

public double SquareRoot(double x)
{
    if (x <= 0.0) 
    {
        throw new ArgumentOutOfRangeException();
    }

迴歸

新的測試通過,但有回歸。 用來通過的測試現在會失敗:

先前通過的單元測試失敗螢幕快照。

尋找並修正錯誤:

public double SquareRoot(double x)
{
    if (x < 0.0)  // not <=
    {
        throw new ArgumentOutOfRangeException();
    }

修正之後,所有測試都會通過:

單元測試總管的螢幕快照,其中包含四個通過的測試。

提示

請確定每個測試都會在您對程式代碼進行的每個變更之後通過。

程式碼涵蓋範圍

在您工作的間隔,最後在簽入程序代碼之前,取得程式代碼涵蓋範圍報告。 這會顯示測試已執行多少程序代碼。

您的小組的目標是涵蓋至少80%。 它們會放寬所產生程序代碼的這項需求,因為很難達到這種類型的程式代碼的高涵蓋範圍。

良好的涵蓋範圍不保證元件的完整功能已經過測試,而且不保證程式代碼適用於每個輸入值範圍。 不過,程式代碼行涵蓋範圍與元件行為空間涵蓋範圍之間有相當密切的關聯性。 因此,良好的涵蓋範圍強化了小組的信心,即他們正在測試他們應該執行的大部分行為。

若要取得程式代碼涵蓋範圍報告,請在 Visual Studio [測試 ] 選單中,選取 [ 分析所有測試的程式代碼涵蓋範圍]。 所有測試都會再次執行。

程式代碼涵蓋範圍結果和 [顯示色彩] 按鈕的螢幕快照。

當您展開報表中的總計時,它會顯示您正在開發的程式代碼具有完整的涵蓋範圍。 這是非常令人滿意的,因為重要的分數是測試中的程序代碼。 發現區段實際上是在測試本身。

切換 [ 顯示程式代碼涵蓋範圍著色 ] 按鈕,您可以看到測試程序代碼的哪些部分尚未練習。 測試中未使用的程式代碼會以橙色醒目提示。 不過,這些區段對於涵蓋範圍而言並不重要,因為它們位於測試程序代碼中,而且只有在偵測到錯誤時才會使用。

若要確認特定測試到達程式碼的特定分支,您可以設定 [顯示程式代碼涵蓋範圍著色 ],然後使用其快捷方式功能表上的 [執行 ] 命令執行單一測試。

何時完成?

您會繼續以小步驟更新程式碼,直到您滿意:

  • 所有可用的單元測試都會通過。

    在具有一組非常大型單元測試的專案中,開發人員可能不切實際地等待他們全部執行。 相反地,專案會操作閘道簽入服務,其中會針對每個存入的擱置集執行所有自動化測試,再合併至來源樹狀結構。 如果執行失敗,簽入會遭到拒絕。 這可讓開發人員在自己的計算機上執行一組最少的單元測試,然後繼續進行其他工作,而不需要執行中斷組建的風險。 如需詳細資訊,請參閱 使用閘道簽入建置程式來驗證變更

  • 程式代碼涵蓋範圍符合小組的標準。 75% 是典型的專案需求。

  • 單元測試會模擬所需的行為的每個層面,包括一般和例外輸入。

  • 您的程序代碼很容易理解和擴充。

符合所有這些準則時,您就可以將程式代碼簽入原始檔控制。

使用單元測試開發程式代碼的原則

在開發程式代碼時套用下列原則:

  • 隨著程式代碼一起開發單元測試,並在開發期間經常執行。 單元測試代表您元件的規格。
  • 除非需求已變更或測試錯誤,否則請勿變更單元測試。 當您擴充程式代碼的功能時,請逐漸新增測試。
  • 目標是測試至少涵蓋 75% 的程式代碼。 查看程式代碼涵蓋範圍結果的間隔,以及簽入原始程式碼之前。
  • 簽入單元測試以及程序代碼,以便由連續或一般伺服器組建執行。
  • 在實際的情況下,針對每個功能,請先撰寫單元測試。 在開發符合程式代碼的程式代碼之前,請先執行此動作。

簽入變更

簽入變更之前,請再次與同事共用您的畫面,讓他們可以非正式且以互動方式檢閱您已建立的內容。 測試會繼續成為您討論的重點,而同事主要對程式代碼的用途感興趣,而不是程式碼的運作方式。 這些同事應該同意您撰寫的內容符合其需求。

簽入您所做的所有變更,包括測試和程式代碼,並將它們與您已完成的工作產生關聯。 簽入會將小組的自動化小組組建系統排入佇列,以使用小組的 CI 組建 建置程式來驗證變更。 此建置程式可藉由在與開發計算機分開的全新環境中建置和測試,協助小組將程式代碼基底中的錯誤降到最低。每次小組所做的變更都不同。

當組建完成時,您會收到通知。 在 [建置結果] 視窗中,您會看到組建成功,並已通過所有測試。

簽入變更

  1. 在 Team Explorer 的 [我的工作] 頁面上,選取 [簽入]。

    從 [我的工作] 簽入的螢幕快照。

  2. 在 [ 擱置的變更 ] 頁面上,確定:

    • 所有相關的變更都會列在 [包含的變更] 中
    • 所有相關的工作項目都會列在 [相關工作專案] 中
  3. 輸入批 ,以協助小組瞭解這些變更的目的,當他們查看變更檔案和資料夾的版本控制歷程記錄時。

  4. 選擇 [簽到]。

    簽入擱置變更的螢幕快照。

持續整合程序代碼

如需如何定義持續整合建置程式的詳細資訊,請參閱 設定 CI 組建。 設定此建置程式之後,您可以選擇收到小組組建結果的通知。

[我的組建] 頁面的螢幕快照,其中包含成功的組建。

如需詳細資訊,請參閱 執行、監視和管理組建

下一步