逐步解說: 使用 [測試總管] 進行測試驅動開發

建立單元測試,以協助讓您的程式碼在經歷漸進的程式碼變更之後仍然能夠正確運作。 有數種架構都可以用來撰寫單元測試,包含由協力廠商所開發的架構。 某些測試架構是專門讓您使用不同的語言或平台來進行測試。 [測試總管] 提供單一介面,讓您使用任一種架構來進行單元測試。 如需 [測試總管] 的詳細資訊,請參閱使用測試總管執行單元測試測試總管常見問題集

本逐步解說示範如何使用 Microsoft 測試架構 (MSTest) 以 C# 來開發受測方法。 您可以輕鬆地將它改寫成其他語言或其他測試架構 (例如 NUnit)。 如需詳細資訊,請參閱安裝協力廠商單元測試架構

建立測試並產生程式碼

  1. 為 .NET 或 .NET Standard 建立一個 C# 類別庫專案。 這個專案將包含我們想要測試的程式碼。 將專案命名為 MyMath

  2. 在同一個方案中,為 .NET 新增一個 MSTest 測試專案。

    在 Visual Studio 2019 版本 16.9 中,MSTest 專案範本名稱為單元測試專案

    將測試專案命名為 MathTests

    New code and test projects

    New code and test projects

  3. 撰寫簡單的測試方法以驗證特定輸入所產生的結果。 將下列程式碼新增至 UnitTest1 類別:

    [TestMethod]
    public void BasicRooterTest()
    {
      // Create an instance to test:
      Rooter rooter = new Rooter();
      // Define a test input and output value:
      double expectedResult = 2.0;
      double input = expectedResult * expectedResult;
      // Run the method under test:
      double actualResult = rooter.SquareRoot(input);
      // Verify the result:
      Assert.AreEqual(expectedResult, actualResult, delta: expectedResult / 100);
    }
    
  4. 從測試程式碼產生類型。

    1. 將游標置於 Rooter 上,然後從燈泡功能表選擇 [產生類型 'Rooter']>[產生新的類型]

      Generate new type quick action

      Generate new type quick action

    2. 在 [產生類型] 對話方塊中,將 [專案] 設為類別庫專案 MyMath,然後選擇 [確定]

      Generate Type dialog box in Visual Studio 2019

      Generate Type dialog box in Visual Studio 2019

  5. 從測試程式碼產生方法。 將游標置於 SquareRoot 上,然後從燈泡功能表選擇 [產生方法 'Rooter.SquareRoot']

  6. 執行單元測試。

    1. 開啟 [測試總管]

      若要從 [測試] 功能表開啟測試總管,選擇 [測試總管]

      若要從 [測試] 功能表開啟測試總管,選擇 [Windows]>[測試總管]

    2. 在 [測試總管] 中,選擇 [全部執行] 按鈕以執行測試。

    接著就會建置方案並執行測試,然後失敗。

  7. 選取測試的名稱。

    測試的詳細資料會顯示在 [測試詳細摘要] 窗格中。

    Test Detail Summary in Test Explorer

    Test Detail Summary in Test Explorer

  8. 選取 [堆疊追蹤] 底下的第一個連結,以跳至測試失敗的位置。

此時,您已建立可以修改的測試和 stub,好讓測試能夠成功。

驗證程式碼變更

  1. Class1.cs 檔案中,改進 SquareRoot 的程式碼:

    public double SquareRoot(double input)
    {
        return input / 2;
    }
    
  2. 在 [測試總管] 中,選擇 [全部執行]

    接著就會建置方案並執行測試,然後通過測試。

    Test Explorer showing a passing test

    Test Explorer showing a passing test

擴充輸入的範圍

若要提高您對於程式碼在任何情況下都能運作的信心,可以加入嘗試各種輸入值的測試。

提示

避免改變已經成功的現有測試, 而改為加入新的測試。 只有在使用者的需求改變時,才變更現有的測試。 這個原則可協助您確保在擴充程式碼時不會失去現有的功能。

  1. 在測試類別中加入以下測試,該測試會嘗試某個範圍的輸入值:

    [TestMethod]
    public void RooterValueRange()
    {
        // Create an instance to test.
        Rooter rooter = new Rooter();
    
        // Try a range of values.
        for (double expected = 1e-8; expected < 1e+8; expected *= 3.2)
        {
            RooterOneValue(rooter, expected);
        }
    }
    
    private void RooterOneValue(Rooter rooter, double expectedResult)
    {
        double input = expectedResult * expectedResult;
        double actualResult = rooter.SquareRoot(input);
        Assert.AreEqual(expectedResult, actualResult, delta: expectedResult / 1000);
    }
    
  2. 在 [測試總管] 中,選擇 [全部執行]

    新的測試失敗了,不過,第一個測試仍然成功。 若要找出失敗點,請選取失敗的測試,然後在 [測試詳細資料摘要] 窗格中查看詳細資料。

  3. 檢查受測方法看看可能是哪裡出錯了。 依照下列方式修改 SquareRoot 程式碼:

    public double SquareRoot(double input)
    {
      double result = input;
      double previousResult = -input;
      while (Math.Abs(previousResult - result) > result / 1000)
      {
        previousResult = result;
        result = result - (result * result - input) / (2 * result);
      }
      return result;
    }
    
  4. 在 [測試總管] 中,選擇 [全部執行]

    現在兩個測試都成功了。

加入例外狀況的測試

  1. 加入新的測試以找出不正確的輸入:

    [TestMethod]
    public void RooterTestNegativeInput()
    {
        Rooter rooter = new Rooter();
        Assert.ThrowsException<ArgumentOutOfRangeException>(() => rooter.SquareRoot(-1));
    }
    
  2. 在 [測試總管] 中,選擇 [全部執行]

    受測方法會產生迴圈現象,必須手動取消。

  3. 選擇 [測試總管] 工具列上的 [取消]

    測試會停止執行。

  4. 透過在方法的開始位置新增以下的 if 陳述式,以修正 SquareRoot 程式碼:

    public double SquareRoot(double input)
    {
        if (input <= 0.0)
        {
            throw new ArgumentOutOfRangeException();
        }
        ...
    
  5. 在 [測試總管] 中,選擇 [全部執行]

    所有測試都成功。

重構受測程式碼

重構程式碼,但不變更測試。

提示

「重構」(Refactoring) 是要讓程式碼的效能變得更好或讓程式碼更容易了解而做的變更。 它不是要變更程式碼的行為,因此並不會變更測試。

我們建議您分開執行重構步驟與擴充功能的步驟。 讓測試保持不變,您就會有信心沒有在重構時不小心引入了 Bug。

  1. 如下所示,變更 SquareRoot 方法中計算 result 的程式碼:

    public double SquareRoot(double input)
    {
        if (input <= 0.0)
        {
            throw new ArgumentOutOfRangeException();
        }
    
        double result = input;
        double previousResult = -input;
        while (Math.Abs(previousResult - result) > result / 1000)
        {
            previousResult = result;
            result = (result + input / result) / 2;
            //was: result = result - (result * result - input) / (2*result);
        }
        return result;
    }
    
  2. 選擇 [全部執行],然後確認仍然通過所有測試。

    Test Explorer showing 3 passed tests

    Test Explorer showing 3 passed tests