共用方式為


使用測試總管針對機器碼執行單元測試

在 Visual Studio 中,您可以建立以 C++ 撰寫的 Unmanaged 程式碼的單元測試。 Unmanaged 程式碼有時稱為機器碼。

下列程序將會包含可協助您開始的基本資訊。 新章節提供更詳細的逐步解說。

用於 Unmanaged 程式碼 DLL 撰寫單元測試。

  1. 使用 [原生測試專案] 範本建立測試的不同的 Visual Studio 專案。

    專案包含一些範例測試程式碼。

  2. 讓 DLL 可供測試專案:

    • 包含 DLL 的外部存取的函式宣告的#include .h 檔案。

      .h 檔案應該包含函式宣告標記 _declspec(dllimport)。 或者,使用 .DEF 檔可以匯出方法。 如需詳細資訊,請參閱匯入和匯出

      您的單元測試可以只能從存取受測的 DLL 匯出函式。

    • 按兩下專案,將參考加入至測試專案。

      在測試專案中的 [內容] ,展開 [一般屬性], [架構和參考],並選取 [新增參考]。

  3. 在測試專案中,建立測試類別和測試方法使用測試巨集和判斷提示如下分類:

    #include "stdafx.h"
    #include <CppUnitTest.h>
    #include "..\MyProjectUnderTest\MyCodeUnderTest.h"
    using namespace Microsoft::VisualStudio::CppUnitTestFramework;
    TEST_CLASS(TestClassName)
    {
    public:
      TEST_METHOD(TestMethodName)
      {
        // Run a function under test here.
        Assert::AreEqual(expectedValue, actualValue, L"message", LINE_INFO());
      }
    }
    
    • Assert 包含可以用來驗證測試結果的數個靜態函式。

    • LINE_INFO() 參數是選擇性的。 在沒有 PDB 檔案處理,它可讓測試執行者識別失敗的位置。

    • 您也可以撰寫測試設定和清除方法。 如需詳細資訊,請開啟 TEST_METHOD 巨集的定義,以及讀取 CppUnitTest.h 的註解。

    • 您不能使用巢狀測試類別。

  4. 使用測試總管執行測試:

    1. 在 [檢視] 功能表上,選擇 [其他視窗], [測試總管]。

    2. 建置 Visual Studio 方案。

    3. 在[測試總管] 中,選取 [全部執行]。

    4. 在測試總管詳細調查任何測試:

      1. 選取測試名稱查看更多詳細資料,例如失敗訊息和堆疊追蹤。

      2. 開啟測試名稱 (例如: 透過按兩下) 移至失敗的位置或測試程式碼。

      3. 在測試的捷徑功能表中,選取 [偵錯選取的測試] 執行偵錯工具中的執行測試。

逐步解說:使用測試總管開發 Unmanaged DLL

您可以調整這個逐步解說開發您的 DLL。 主要的步驟如下。

  1. 建立測試專案。 測試在從 DLL 的個別專案建立您開發。

  2. 建立一個DLL專案. 這個逐步解說中建立新的 DLL,不過,測試現有 DLL 方法很類似。

  3. 讓 DLL 函式看見測試。

  4. 重複擴大測試。 我們建議「紅色、綠色重構」循環,開發程式碼是由測試所引導。

  5. 偵錯失敗的測試。 您可以在偵錯模式時執行測試。

  6. 當測試仍然沒有變更時重構。 重構是改善程式碼的結構,而不會變更它的外部行為。 您可以將它改善程式碼的效能、擴充性、可讀性。 因為這個檢視不會變更行為,當決定重構變更程式碼時,並不會變更測試。 測試確保當您重構時不會產生錯誤。 如果沒有測試,您可以更有信心去進行這類變更。

  7. 檢查涵蓋範圍。 執行更多程式碼時,單元測試更有用。 您可以尋找那些是用測試來使用您的程式碼部分。

  8. 從外部資源的隔離單位。 通常, DLL 會根據您開發,例如其他 DLL,資料庫系統,或是遠端子系統的其他元件。 測試隔離的每單位的相依性會很有用。 外部元件可以緩慢執行您的測試。 在開發期間,其他元件可能不完整。

建立單元測試專案。

  1. 在 [檔案] 功能表上,選擇 [新增專案]。

    在對話方塊中,展開 [已安裝的], [樣板], [Visual C++], [測試]。

    選取 [原生測試專案] 範本。

    在這個逐步解說中,測試專案會命名為NativeRooterTest。

    建立 C++ 單元測試專案

  2. 在新專案中,檢查unittest1.cpp

    包含 TEST_CLASS 和 TEST_METHOD 的測試專案

    請注意:

    • 每個測試由TEST_METHOD(YourTestName){...}所定義。

      您不需要撰寫傳統函式簽章。 簽章由 TEST_METHOD 巨集建立。 巨集產生傳回 void 函式的執行個體。 它也會傳回關於測試方法的相關資訊的靜態函式。 這個資訊允許測試總管尋找方法。

    • 藉由TEST_CLASS(YourClassName){...}將測試方法聚集成類別.

      當測試執行時,每一個測試類別建立執行個體。 測試方法會按照未指定的順序呼叫。 您可以在每個模組、類別或方法之後,定義要叫用的特殊方法。 如需詳細資訊,請參閱 組合管理 C++測試.。

  3. 驗證在測試總管中執行的測試:

    1. 插入某些測試程式碼:

      TEST_METHOD(TestMethod1)
      {
      Assert::AreEqual(1,1);
      }
      

      請注意 Assert 類別提供可用來確認測試方法之結果的幾個靜態方法。

    2. 在 [測試] 功能表上,選擇 [執行], [所有測試]。

      測試建置並執行。

      測試瀏覽器出現。

      測試隨即出現在 [通過的測試] 之下。

      [單元測試總管] 中有一個測試成功

建立未處理的DLL 應用程式專案。

  1. 您可以建立 [Visual C++] 專案,藉由 [Win32 專案] 範本。

    在這個逐步解說中,專案被命名為 RootFinder。

    建立 C++ Win32 專案

  2. 在 Win32 應用程式精靈中選取 [DLL] 和 [匯出符號。] 。

    [匯出符號] 選項會產生可用來宣告匯出方法的方便巨集。

    已選取 [DLL] 和 [匯出符號] 的 C++ 專案精靈設定

  3. 宣告在主要 .h 檔的匯出函式:

    新的 DLL 程式碼專案以及包含 API 巨集的 .h 檔案

    這個陳述式 __declspec(dllexport) 造成類別的公用和顯示在 DLL 以外的類別的受保護成員。 如需詳細資訊,請參閱使用 C++ 類別] 中 dllimport 和 dllexport

  4. 在主要 .cpp 檔案中,加入最小的主體到函式:

    // Find the square root of a number.
    double CRootFinder::SquareRoot(double v)
    {
      return 0.0;
    }
    

結合測試專案到 DLL 專案

  1. 按兩下專案,將參考加入至測試專案:

    1. 開啟測試專案屬性並選取 [一般屬性], [架構和參考]。

      C++ 專案屬性 - [架構和參考]

    2. 按一下 [加入新參考]。

      在 [新增參考] 對話方塊中,選取 DLL 專案並選取 [新增]。

      C++ 專案屬性 - [加入新參考]

  2. 在主要單元測試 .cpp 檔,包含DLL 程式碼的 .h 檔案:

    #include "..\RootFinder\RootFinder.h"
    
  3. 加入使用匯出函式的基本測試:

    TEST_METHOD(BasicTest)
    {
    CRootFinder rooter;
    Assert::AreEqual(
    // Expected value:
    0.0, 
    // Actual value:
    rooter.SquareRoot(0.0), 
    // Tolerance:
    0.01,
    // Message:
    L"Basic test failed",
    // Line number - used if there is no PDB file:
    LINE_INFO());
    }
    
  4. 建置方案。

    新的測試隨即出現在測試總管。

  5. 在[測試總管] 中,選取 [全部執行]。

    [單元測試總管] -「基本測試」成功

您已經設定了測試和程式碼專案和驗證您是否可以執行在程式碼專案之函式的測試。 現在您可以開始撰寫實際測試和程式碼。

請重複擴大測試並讓它們傳遞

  1. 加入新的測試:

    TEST_METHOD(RangeTest)
    {
      CRootFinder rooter;
      for (double v = 1e-6; v < 1e6; v = v * 3.2)
      {
        double actual = rooter.SquareRoot(v*v);
        Assert::AreEqual(v, actual, v/1000);
      }
    }
    

    提示

    我們建議您不要變更成功的測試。相反地,請加入新的測試,更新程式碼,使測試成功,然後加入另一個測試,依此類推。

    當您的使用者變更他們的需求時,請停用不再是正確的測試。撰寫新的測試並將這些工作以相同的方式一次加入一個項目。

  2. 建置方案,然後在測試總館中,選取 [全部執行]。

    新測試失敗

    RangeTest 失敗

    提示

    確認每個在您撰寫後的測試失敗。這有助於避免撰寫從未失敗的測試簡單的錯誤。

  3. 引發正在測試的程式碼,以便新測試成功:

    #include <math.h>
    ...
    double CRootFinder::SquareRoot(double v)
    {
      double result = v;
      double diff = v;
      while (diff > result/1000)
      {
        double oldResult = result;
        result = result - (result*result - v)/(2*result);
        diff = abs (oldResult - result);
      }
      return result;
    }
    
  4. 建置方案,然後在測試總管中,選取 [全部執行]。

    兩個測試都通過。

    [單元測試總管] -「範圍測試」成功

    提示

    一次增加一個測試去開發程式碼。確定所有測試每次反覆運算後傳遞。

偵錯失敗的測試。

  1. 加入另一個測試:

    #include <stdexcept>
    ...
    // Verify that negative inputs throw an exception.
    TEST_METHOD(NegativeRangeTest)
    {
      wchar_t message[200];
      CRootFinder rooter;
      for (double v = -0.1; v > -3.0; v = v - 0.5)
      {
        try 
        {
          // Should raise an exception:
          double result = rooter.SquareRoot(v);
    
          _swprintf(message, L"No exception for input %g", v);
          Assert::Fail(message, LINE_INFO());
        }
        catch (std::out_of_range ex)
        {
          continue; // Correct exception.
        }
        catch (...)
        {
          _swprintf(message, L"Incorrect exception for %g", v);
          Assert::Fail(message, LINE_INFO());
        }
      }
    }
    
  2. 建置解決方法並且選擇[執行全部]。

  3. 開啟(或按兩下) 失敗的測試。

    失敗的判斷提示會反白顯示。 錯誤訊息會顯示在測試總管詳細資料窗格中。

    NegativeRangeTests 失敗

  4. 請參閱針對測試失敗的原因失敗,逐步執行函式:

    1. 中斷點會設定在函式的開頭。

    2. 在失敗的測試的捷徑功能表中,選取 [偵測選取的測試]。

      當執行在中斷點停止時,請逐步執行程式碼。

  5. 在您開發的功能中插入程式碼:

    #include <stdexcept>
    ...
    double CRootFinder::SquareRoot(double v)
    {
        // Validate parameter:
        if (v < 0.0) 
        {
          throw std::out_of_range("Can't do square roots of negatives");
        }
    
  6. 所有測試都成功

    所有測試都成功

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

  1. 簡化了 SquareRoot 函式的中央計算:

    // old code:
    //   result = result - (result*result - v)/(2*result);
    // new code:
         result = (result + v/result)/2.0;
    
  2. 建立方案,並選取 [全部執行],確定沒有造成錯誤。

    提示

    好一組單元測試在當您變更程式碼時,會確認您未引入 Bug。

    將重構變更和其他變更分開處理。

後續步驟

  • **隔離性。**大部分的 DLL 相依於其他子系統 ,例如資料庫和其他 DLL。 這些其他元件通常會平行開發。 若要讓單元測試執行,而其他元件無法使用,則必須以模擬或

  • 組建驗證測試您可以在小組的組建伺服器中在設定的間隔中執行的測試。 當多個小組成員的工作產生關聯時,這可確保不會產生錯誤。

  • **簽入測試。**您可以要求某些測試執行時,在每個小組成員簽入程式碼加入至原始檔控制項之前。 這通常是完整的子集組建驗證測試。

    您也可以要求程式碼涵蓋範圍中的最小層級。

請參閱

工作

Walkthrough: Creating and Using a Dynamic Link Library (C++)

概念

匯入和匯出

其他資源

Managed 和 Unmanaged 程式碼互通性概觀

偵錯機器碼