共用方式為


本文章是由機器翻譯。

生產測試

使用 Pex 對舊版程式碼進行自動化單元測試

Nikhil Sachdeva

下載程式碼範例

我先前的生活中我是顧問。我的用戶端前置銀行的其中一個想要將其貸款原始程序自動化。銀行已經有一個系統,在包含 Windows 架構應用程式]、 [專屬的後端] 及 [也是其解決方案的核心的大型主機系統的地方。我的工作是一組應用程式開發的帳戶除法與整合現有的系統。

用戶端有內建傳達大型主機的 Web 服務。第一次似乎很簡單。我只執行一項是連接到服務、 取得的資訊,將它傳遞給新帳戶應用程式。但 ’s 永遠不會那麼簡單。

在實作階段我發現新的系統必須有一個貸款召集人] 屬性,但是 Web 服務 GetLoanDetails 方法未傳回該資訊。結果,服務由開發人員在公司不再 ’s 建立年前。銀行已使用而不需任何修改服務因為它有許多層級,而且每個人都是恐怕破壞的東西。

該 Web 服務是舊版的程式碼。

最後,我們建置輕量服務包裝函式因此新的系統可能呼叫的任何新的資訊,並繼續使用舊服務。它本來應該是更容易地修改現存的服務,如果有被遵循一致且可測試的設計。

保持最新程式碼

傳統的程式碼是亦開發在過去,並仍然可用但在很難維護,並變更。通常維持這些系統的原因是周圍的成本和時間參與建置類似的新系統 — 雖然有時候是缺乏認知有關目前的程式碼撰寫努力的未來的含意。

事實是經過一段時間的程式碼開始 rot。這可以是因為需求的變更、 不-非常-嗯-思考-透過設計、 套用的 anti-patterns 或缺乏適當的測試。最終結果是難以維護且難以變更的程式碼。

有許多方法來防止程式碼書,但其中一個最有效可以是撰寫可測試的程式碼,然後產生程式碼的足夠單元測試。單元測試會當做持續探查發現路徑在系統、 識別麻煩的錯誤,並提供指標是否變更導入子系統有良好或不正確的效果,整體軟體上的代理程式。單元測試可讓開發人員的信心任何程式碼變更不會引入任何回復。

不用說,建立及維護良好的單元測試套件可以在本身的挑戰。它 ’s 可能會得到撰寫更多的程式碼的測試套件比受測試程式碼。另一項挑戰是在程式碼中的相依性。愈複雜的解決方案可能之間尋找更多的相依性類別。Mock 和 Stub 可辨識方法來移除這些相依性,並測試程式碼中隔離,但這些需要額外的知識和經驗從開發人員建立有效的單元測試。

Pex 救

Pex ( research.microsoft.com/projects/pex/ ) 是由 Microsoft 參考資料,以自動並有系統地產生最基本的執行有限數量的有限路徑所需的測試輸入開發的工具。Pex 會自動產生小型單元測試套件,並且涵蓋大量程式碼和判斷提示。

Pex 會尋找有趣的輸入輸出值,您可以儲存成具備高程式碼涵蓋範圍的小型測試套件的方法。Pex 會執行使用系統化分析搜尋界限條件、 例外狀況和您馬上可以偵錯判斷提示失敗。Pex 也可讓參數化的單元測試 (PUT)、 的單元測試的延伸可以降低測試維護成本,並利用動態探查透過受測建立測試組合,涵蓋大部份分支執行的程式碼的符號執行。

一個 PUT 是只是一種方法,以取得參數、 呼叫受測試程式碼並陳述判斷提示。範例 PUT 看起來會像這樣:

void AddItem(List<int> list, int item) {

  list.Add(item);

  Assert.True(list[list.Count - 1] == item);

}

衍生 PUT 概念更廣泛的專門用語,稱為 「 資料導向測試 (DDT),從其已使用一段很長的時間,在傳統的單元測試中進行測試可重複。 因為傳統的單元測試,就會關閉本質,唯一的方式提供給他們的輸入的值,是透過外部來源 (例如 XML 檔、 試算表或資料庫。 雖然 DDT 方法很適合,沒有維護與變更外部資料] 檔案所需的額外負荷,而建議事項是取決於系統的相關的開發人員 ’s 知識。

Pex 不依賴外部來源而是透過並提供測試方法的輸入將值傳遞至對應 PUT。 因為 [PUT 開啟的方法可以排列接受任何輸入的數目。 此外,Pex 不會如 [PUT 產生隨機的值。 它依賴 introspection 受測方法,並會產生有意義的值根據界限條件、 可接受的型別值及呼叫 Z3 的先進的條件約束規劃求解等因素 ( research.microsoft.com/en-us/um/redmond/projects/z3/ )。 如此可確保涵蓋受測方法的所有相關的路徑。

Pex 美麗是它會產生傳統的單元測試,從 [PUT。 您可以直接在一個單位像 MSTest Visual Studio 中測試架構而不需任何修改執行這些單元測試。 Pex 提供產生像 NUnit 或 xUnit.NET 架構的單元測試的擴充功能。 您也可以建立您自己的自訂延伸模組。 一個 Pex 產生傳統的單元測試看起來像是這樣:

[TestMethod]

[PexGeneratedBy(typeof(TestClass))]

void AddItem01() {

  AddItem(new List<int>(), 0);

}

動態符號執行是 Pex ’s 答案探勘測試。使用這項技術,Pex 執行程式碼很多次了解程式行為。它會監視控制項和資料流,並建置測試輸入的限制式系統。

單元測試 Pex

第一個步驟是建立受測試程式碼 PUT。開發人員或使用 Visual Studio 增益集的 Pex [PUT 可能以手動方式產生。您可以自訂 [PUT 藉由修改參數、 加入 Pex 工廠和 Stub、 整合與 Mock、 依此類推加入判斷提示。Pex 工廠和 Stub 會在本文稍後說明。

目前在 Visual Studio 增益集,以便 Pex 只在 C# 中建立將但受測程式碼可以是任何.NET 語言。

PUT 設定好之後第二個步驟是執行 Pex 探索。這是其中 Pex 會其魔法。它會分析以識別哪些要測試 PUT。接著會檢查受測試程式碼的開始通過每個分支,並評估可能的輸入的值。Pex 重複執行受測試程式碼。在每個回合後它會挑選子目錄,所未涵蓋在先前,組建限制系統 (透過測試輸入述詞) 來達到該分支然後如果有的話,使用條件約束規劃求解來決定新的測試輸入。測試會以新的輸入一次執行,並且重複此程序。

在每個執行 Pex 可能會發現新的程式碼和深入實作。以此方式 Pex 會探索程式碼的行為。

同時瀏覽程式碼,Pex 會產生單元測試組合,其中包含涵蓋 Pex 可以練習的所有子目錄的測試。這些測試是可以在 Visual Studio 測試編輯器中執行的標準 
unit 測試。如果您發現較低的涵蓋範圍的某些區段,您可能會認為的修訂您的程式碼以重整,然後套用相同的循環一次以達到更高的程式碼涵蓋範圍和更廣泛的測試套件。

Pex 可協助降低的投入量,並處理的舊版程式碼的相關時間。Pex 會自動探索不同分支和程式碼路徑,因為 don’t 必須瞭解的整個程式碼基底的所有詳細資料。另一個好處就是開發人員可以運作 PUT 層級。撰寫一個 PUT 十分通常比寫入關閉單元測試,因為您專注於問題案例,而非所有可能測試案例的功能更簡單。

放入 Pex 運作

let’s 使用 Pex 在舊版的程式碼片段,並看看它如何協助讓程式碼更易於維護且容易測試。

Fabrikam、 basketballs 和 baseballs,了前置製造廠商提供線上的入口網站使用者可以檢視可用的產品並放置針對這些產品的訂單。存貨詳細資料是透過提供資料存放區和等 HasInventory] 及 [移除作業的連線能力的倉儲元件存取的自訂資料存放區中。「 訂單 」 元件提供處理根據產品的順序和數量由使用者傳遞的 Fill 方法。

訂單和倉儲元件緊密聯繫到彼此。這些元件都來自年前,而且沒有目前的員工有徹底瞭解系統。沒有單元測試已建立在開發期間,而且可能為一個結果則元件便是很不穩定。目前的設計 的 圖 1 所示。

圖 1 A 傳統訂單履約系統

Order 類別的 Fill 方法看起來會像這樣:

public class Order {

  public bool Fill(Product product, int quantity) {

    // Check if WareHouse has any inventory

    Warehouse wareHouse = new Warehouse();

    if (wareHouse.HasInventory(product, quantity)) {

      // Subtract the quantity from the product in the warehouse

      wareHouse.Remove(product, quantity);

      return true;

    }



    return false;

  }

}

有幾個索引鍵的項目,需要注意這裡。第一次,順序與倉儲會緊密結合。類別的實作,使它們較不可擴充且難以使用 Mock 或 Stub 架構而定。沒有單元測試可以讓所做的任何變更可能會引入回復導致的不穩定的系統。

很久以前撰寫倉儲元件,並且目前的開發小組有不知道如何變更它或任何變更的隱含意義。若要以便更複雜的事情順序無法使用其他實作的倉儲而不需修改。

let’s 嘗試重整程式碼,然後使用 Pex 產生單元測試。我將會重整倉儲] 和 [順序] 物件,然後建立的順序類別 Fill 方法的單元測試。

重整舊版程式碼顯然是一項挑戰。在這些情況下的一種方法可能是至少讓程式碼可測試,如此可產生足夠的單元測試。我將會套用只淒涼最少模式,讓程式碼可測試。

第一個問題是順序使用特定實作的倉儲。這使得困難提高倉儲實作,從 [順序]。let’s 修改程式碼,使其更具彈性且可測試一個位元。

我開始建立介面 IWareHouse 並修改倉儲物件來實作這個介面。任何新的倉儲會要求要實作這個介面。

因為順序在倉儲上直接相依性,它們是緊密結合。我使用相依性的插入開啟的行為的擴充性類別。使用這種方法,IWareHouse 執行個體就會傳遞至訂單在執行階段。新的設計 的 圖 2 所示。

圖 2 修訂系統與 IWareHouse

新的順序類別 的 圖 3 所示。

圖 3 修訂順序類別

public class Order {

  readonly IWareHouse orderWareHouse;



  // Use constructor injection to provide a wareHouse object

  public Order(IWareHouse wareHouse) {

    this.orderWareHouse = wareHouse;

  }



  public bool Fill(Product product, int quantity) {

    // Check if WareHouse has any inventory

    if (this.orderWareHouse.HasInventory(product, quantity)) {

      // Update the quantity for the product

      this.orderWareHouse.Remove(product, quantity);

      return true;

    }

    return false;

  }

}

建立參數化的單元測試

let’s 現在使用 Pex 產生重整的程式碼的測試。Pex 提供可產生一個 Visual Studio 增益集將放輕鬆。以滑鼠右鍵按一下專案、 類別或方法的 [將需要產生按一下 Pex | 建立參數化單元測試 Stub。我開始藉由選取的順序類別 Fill 方法。

Pex 可讓您選取現有的單元測試專案,或建立新。它也提供您選項來篩選測試根據方法或型別名稱 (請參閱 的 圖 4)。

圖 4 的 新增 Pex 專案設定

Pex 會產生下列 PUT 的 Fill 方法。

[PexClass(typeof(Order))]

[TestClass]

public partial class OrderTest {

  [PexMethod]

  public bool Fill([PexAssumeUnderTest] Order target, 

    Product product, int quantity) {



    // Create product factory for Product

    bool result = target.Fill(product, quantity);

    return result;

  }

}

OrderTest 不只是一般 TestClass ; 已被使用指出它已由 Pex 建立一個 PexClass 屬性加註。您會在標準的 Visual Studio 單元測試中所預期,目前正在產生沒有 TestMethod。而是,您有一個 PexMethod。這個方法會參數化的單元測試。稍後,讓 Pex 探索受測程式碼時, 就會建立另一個包含具有 TestMethod 屬性加註的標準的單元測試的部分類別。這些產生的測試將會透過 Visual Studio 測試編輯器可存取。

請注意 PUT 的 Fill 方法接受三個參數。

[PexAssumeUnderTest] 順序目標

這是受測試本身類別。PexAssumeUnderTest 屬性會告知 Pex 它應該只傳遞確切的指定型別的非 Null 值。

產品的產品

這是產品基底類別。Pex 會試著自動建立產品類別的執行個體。更細微的控制,您可以使用 Factory 方法提供 Pex。Pex 會使用這些工廠來建立複雜的類別的執行個體。

int 數量

Pex 會提供的數量根據受測方法的值。它會嘗試插入測試的有意義的值,而不是垃圾的值。

Pex 工廠

前面提之前 Pex 會使用條件約束規劃求解來決定參數的新測試輸入。輸入值可以是標準的.NET 型別或自訂的商業項目。在探索,期間 Pex 實際上可以建立這些型別的執行個體讓受測試程式可以行為不同有趣的方式。如果類別為可見,且具有看得見的預設建構函式,Pex 可以建立類別的執行個體。如果所有欄位都是可見的它可以為他們也產生值。不過,如果欄位是封裝內容,或是不會公開給外面的世界,Pex 會要求建立物件,以達到較佳的程式碼涵蓋範圍的說明。

Pex 提供建立及連結所需的物件 Pex 探索到的兩個攔截。使用者可以為複雜物件提供工廠,以便 Pex 可以瀏覽不同的物件狀態。透過 Pex 工廠方法來達成。能透過這類工廠建立型別稱為 explorable 型別。我們會在本文中使用這種方法。

另一種方法是定義物件的私用欄位的恆定性,所以 Pex 可以直接製造不同的物件狀態。

光顧範例的案例如果執行產生的參數化測試的 Pex 探索,Pex 探索結果] 視窗會顯示訊息 「 2 物件的建立 」。這並不是一項錯誤。在探索,期間 Pex 遇到複雜的類別 (本例中的 [順序]),並建立對該類別的預設工廠。Pex 被需要這個工廠更能瞭解程式行為。

Pex 所建立的預設工廠是類別的香草實作必要。您可以調整提供您自己的自訂實作這個工廠。按一下程式碼注入您的專案的工廠接受/編輯上 (請參閱 的 [圖 5])。或者,您可以使用 PexFactoryMethod 屬性加註的靜態方法來建立靜態類別。時探索,Pex 將在測試專案,以供具有這個屬性的方法具有任何靜態類別中搜尋,並適當地使用它們。

圖 5 建立預設工廠

OrderFactory 看起來會像這樣:

public static partial class OrderFactory {

  [PexFactoryMethod(typeof(Order))]

  public static Order Create(IWareHouse wareHouseIWareHouse) {

        

    Order order = new Order(wareHouseIWareHouse);

    return order;

  }

}

如果您在其他組件中撰寫原廠方法,您可以告訴 Pex 使用組件層級 PexExplorableFromFactoriesFromType 或 PexExplorableFromFactoriesFromAssembly 屬性,例如在宣告式的方式使用它們。

[assembly: PexExplorableFromFactoriesFromType(

  typeof(MyTypeInAnotherAssemblyContainingFactories))]

Pex 會建立極少數的測試,或藉由只應該在建立的物件上擲回一個 NullReferenceException 建立有趣的測試失敗時,如果這是很好的指示 Pex 可能需要自訂的工廠。否則,Pex 會隨附在許多情況下建立工作的物件工廠的啟發式一組。

Pex Stub 架構

在軟體開發過程中測試虛設常式的概念是指虛設的實作,使其可以用來取代可能是複雜的元件,以利於進行測試。當 Stub 的概念簡單時大部分現有的架構,可以幫助建立和維護虛設的實作是實際上相當複雜。Pex 小組已發展出新的輕量型架構,它們只是呼叫 Stub。Stub 會產生.NET 介面和非密封類別的 Stub 型別。

在這個架構型別 T 的虛設常式會提供的每個 T,抽象成員的預設實作和機制來動態指定的每一成員的自訂實作。(選擇性地,Stub 也可能會產生之非抽象虛擬成員)。虛設常式型別會產生為 C# 程式碼。架構只依賴委派以動態方式指定 Stub 成員的行為。Stub 支援.NET Framework 2.0 和較高,並整合與 Visual Studio 2008 更高。

在我的範例案例中 [順序] 型別會有相依性,倉儲物件上。請記得我重整程式碼來實作相依性的插入,以便從提供倉儲存取外部 Order 型別。進來方便建立 「 Stub 時。

建立一個虛設常式是相當簡單。您只需要為.stubx 檔案。如果您建立測試專案,透過 Pex,您應該已經有它。如果沒有,從 Visual Studio 內建立這個檔案。測試專案上按一下滑鼠右鍵,然後選取 [加入新項目。Stub 範本是可用 (請參閱 的 圖 6)。

圖 6 建立新的 Stub

檔案會顯示為標準的 XML 檔案,Visual Studio 中。組件] 項目中指定組件的 Stub 需要被建立並儲存.stubx 檔案的名稱:

<Stubs xmlns="https://schemas.microsoft.com/stubs/2008/">

  <Assembly Name="FabrikamSports" />

</Stubs>

Pex 會自動在組件中建立所有類型的必要虛設常式方法。

產生的 Stub 方法有提供 stubbed 實作勾點的對應委派欄位。預設情況下,Pex 會為委派提供實作。您也可以提供 Lambda 運算式可以附加至委派的行為,或使用 PexChoose 型別,以便讓 Pex 會自動產生之方法的值。

比方說為 HasInventory 方法提供選擇,我可以有像這樣:

var wareHouse = new SIWareHouse() {

  HasInventoryProductInt32 = (p, q) => {

    Assert.IsNotNull(p);

    Assert.IsTrue(q > 0);

    return products.GetItem(p) >= q;

  }

};

在實際上使用 PexChoose 是已經 Stub 預設的行為時使用 Pex,Pex 所建立的測試專案中包含 
following 組件層級的屬性:

[assembly: PexChooseAsStubFallbackBehavior]

Pex Stub 架構會產生 SIWareHouse 型別。 它會實作 IWareHouse 介面。 let’s 會再進一步 Pex 所產生的 SIWareHouse 虛設常式的程式碼。 原始程式碼.stubx 檔案建立名稱為 < StubsxFilename > 的部分類別中為.designer.cs 的 圖 7 所示。

圖 7 Pex 產生 IWareHouse Stub

/// <summary>Stub of method System.Boolean 

/// FabrikamSports.IWareHouse.HasInventory(

/// FabrikamSports.Product product, System.Int32 quantity)

/// </summary>

[System.Diagnostics.DebuggerHidden]

bool FabrikamSports.IWareHouse.HasInventory(

  FabrikamSports.Product product, int quantity) {

    

  StubDelegates.Func<FabrikamSports.Product, int, bool> sh

    = this.HasInventory;

  if (sh != (StubDelegates.Func<FabrikamSports.Product, 

    int, bool>)null)

    return sh.Invoke(product, quantity);

  else {

    var stub = base.FallbackBehavior;

    return stub.Result<FabrikamSports.Stubs.SIWareHouse, 

      bool>(this);

  }

}



/// <summary>Stub of method System.Boolean 

/// FabrikamSports.IWareHouse.HasInventory(

/// FabrikamSports.Product product, System.Int32 quantity)

/// </summary>

public StubDelegates.Func<FabrikamSports.Product, int, bool> HasInventory;

Stub 建立 HasInventory 方法的公用委派欄位,並且在 HasInventory 實作中叫用。 如果沒有實作是可用的 Pex 會呼叫 FallBackBehaviour.Result 方法可以使用 PexChoose,如果在 [組件:PexChooseAsStubFallbackBehavior] 存在,且會擲回一個 StubNotImplementedException 否則。

若要用以 IWareHouse stubbed 的實作我會稍微調整 Parameterized 單元測試。 我已經修改順序類別中,以將能在其建構函式中進行 IWareHouse 實作。 我現在建立 SIWareHouse 執行個體,然後將,傳遞至 Order 類別,因此它使用 IWareHouse 方法的自訂的實作。 修訂的 PUT 如下所示:

[PexMethod]

public bool Fill(Product product, int quantity) {

  // Customize the default implementation of SIWareHouse

  var wareHouse = new SIWareHouse() {

    HasInventoryProductInt32 = (p, q) => 

    PexChoose.FromCall(this).ChooseValue<bool>(

    "return value")

  };



  var target = new Order(wareHouse);

  // act

  bool result = target.Fill(product, quantity);

  return result;

}

Stub 實際上會自動提供虛設常式的方法的預設實作,因此您可能有只需執行而不需任何修改也 PUT。

參數化的模型

為 stubbed 實作的更細微控制項,Pex 會支援一種概念,稱為參數化的模型。 這是要寫入沒有固定的行為的一個特定的 Stub 的方法。 Pex 提供透過這個概念的抽象是開發人員並不需要擔心實作各種變化。 Pex 會探索不同根據它們由受測程式碼之方法的傳回值。 參數化的模型是功能強大的功能,可讓您取得完整的控制權,透過 Stub 在同一時間讓 Pex 評估的輸入參數變數的值雖然應該處理方式。

參數化的模式,用於 IWareHouse 可能看起來 的 [圖 8] 中的程式碼。

圖 8 參數化模型 IWareHouse

public sealed class PWareHouse : IWareHouse {

  PexChosenIndexedValue<Product, int> products;



  public PWareHouse() {

    this.products = 

      new PexChosenIndexedValue<Product, int>(

      this, "Products", quantity => quantity >= 0);

  }



  public bool HasInventory(Product product, int quantity) {

    int availableQuantity = this.products.GetItem(product);

    return quantity - availableQuantity > 0;

  }



  public void Remove(Product product, int quantity) {

    int availableQuantity = 

    this.products.GetItem(product); 

    this.products.SetItem(product, 

      availableQuantity - quantity);

  }

}

基本上是我已經建立 IWareHouse,我自己 stubbed 的實作,但是請注意我不數量及產品提供的值。相反地,我讓 Pex 產生這些值。PexChosenIndexedValue 自動提供值之物件允許使用 Variant 參數值只有一個 stubbed 的實作。

為了簡單起見,我將讓 Pex 提供 HasInventory 實作 IWareHouse 型別的。我將加入程式碼至 OrderFactory 我稍早建立的類別。每次 Pex 依建立順序執行個體它將會使用 stubbed 的倉儲執行個體。

Moles

到目前為止,我著重在兩個原則 — 重整程式碼,以便可測試,然後使用 Pex 產生單元測試。這種方法可讓您清除程式碼最後會導致更易於維護軟體。但是,重整舊版程式碼可能會在本身的大挑戰。可以有許多組織或技術限制可能會造成程式開發人員從重整目前的原始碼。您如何處理這?

在舊版的程式碼是難重整的案例是方法應該至少建立商務邏輯的足夠單元測試,讓您可以驗證系統的每個模組的強固性。模擬 (Mock) 架構像 TypeMock (learn.typemock.com) 已經周圍一段時間。它們可讓您建立單元測試,而不實際修改程式碼基底。特別為大型的舊版程式碼基底,確實很有幫助這種方法。

Pex 會具有稱為 Moles 一個功能,可讓您達成相同的目標。它可讓您不需要實際重整原始程式碼產生舊版的程式碼的 Pex 單元測試。moles 真的是用來測試否則 untestable 部分的系統例如靜態方法和密封的類別。

moles 的運作方式都很類似 Stub 至:Pex 程式碼-產生 mole 公開 (Expose) 每一種方法的委派屬性的型別。您可以附加一個委派,然後將附加一個 mole。在該點所有自訂的委派取得有線向上神奇地由 Pex 程式碼剖析工具。

Pex 會自動在.stubx 檔案中建立所指定的靜態、 密封和公用介面的 moles。Pex Mole 看起來非常類似一個 Stub 輸入 (請參閱下載範例程式碼)。

使用 moles 是相當簡單。您可以提供 Mole 方法實作中,並用您 PUT 中。注意到因為 Mole 會在執行階段插入 stubbed 的實作,您做不了若要變更您程式碼基底,才能產生單元測試使用 moles 的根本。

let’s 使用 moles 上傳統的 Fill 方法:

public class Order {

  public bool Fill(Product product, int quantity) {

    // Check if warehouse has any inventory

    Warehouse wareHouse = new Warehouse();

    if (wareHouse.HasInventory(product, quantity)) {

      // Subtract the quantity from the product 

      // in the warehouse

      wareHouse.Remove(product, quantity);

      return true;

    }



    return false;

  }

}

我要建立會運用 Mole 型別 (請參閱 的 圖 9) 之 Fill 方法 PUT。

圖 9 使用填滿上的 Mole 類型

[PexMethod]

public bool Fill([PexAssumeUnderTest]Order target,

  Product product, int quantity) {



  var products = new PexChosenIndexedValue<Product, int>(

    this, "products");

  // Attach a mole of WareHouse type

  var wareHouse = new MWarehouse {

    HasInventoryProductInt32 = (p, q) => {

      Assert.IsNotNull(p);

      return products.GetItem(p) >= q;

    }

  };



  // Run the fill method for the lifetime of the mole 

  // so it uses MWareHouse

  bool result = target.Fill(product, quantity);

  return result;

}

MWareHouse 是由建立自動 Pex 產生 Stub 和 moles 透過.stubx 檔案時的 Mole 型別。 我為 MWareHouse] 型別的 HasInventory 委派提供自訂的實作,然後呼叫 Fill 方法。 請注意 nowhere 執行我提供倉儲物件至 Order 型別建構函式的實作。 Pex 會在執行階段,以附加至 Order 型別 MWareHouse 執行個體。 PUT 的存留期 (Lifetime) 將區塊內部撰寫任何程式碼會運用 MWareHouse 型別實作倉儲實作將需要在舊版的程式碼的地方。

Pex 會產生使用 moles 的傳統的單元測試時, 其會連接屬性 [HostType(“Pex”)] 它們,這樣它們會執行與 Pex 分析工具可讓 moles 成為使用中。

總結

我討論有關 Pex 以及如何使用它們的各種功能。 現在它 ’s 時間實際上執行 [PUT 並觀察其結果。 若要執行的訂單的 Fill 方法的研究,只是在 [PUT 上按一下滑鼠右鍵,然後選取執行 Pex 探索。 選擇性地,您就可以在一個類別或整個專案上執行研究。

執行 Pex 探索時部分類別會建立沿著 PUT 類別檔案中。 此部分類別包含 Pex 會替 [PUT 產生的所有標準的單元測試。 填滿] 方法 Pex 會產生使用各種不同的測試輸入標準的單元測試。 測試的 圖 10 所示。

圖 10 的 Pex 產生的測試

[TestMethod]

[PexGeneratedBy(typeof(OrderTest))]

public void Fill15()

{

    Warehouse warehouse;

    Order order;

    Product product;

    bool b;

    warehouse = new Warehouse();

    order = OrderFactory.Create((IWareHouse)warehouse);

    product = new Product("Base ball", (string)null);

    b = this.Fill(order, product, 0);

    Assert.AreEqual<bool>(true, b);

}

[TestMethod]

[PexGeneratedBy(typeof(OrderTest))]

public void Fill16()

{

    Warehouse warehouse;

    Order order;

    Product product;

    bool b;

    warehouse = new Warehouse();

    order = OrderFactory.Create((IWareHouse)warehouse);

    product = new Product("Basket Ball", (string)null);

    b = this.Fill(order, product, 0);

    Assert.AreEqual<bool>(true, b);

}

[TestMethod]

[PexGeneratedBy(typeof(OrderTest))]

public void Fill17()

{

    Warehouse warehouse;

    Order order;

    Product product;

    bool b;

    warehouse = new Warehouse();

    order = OrderFactory.Create((IWareHouse)warehouse);

    product = new Product((string)null, (string)null);

    b = this.Fill(order, product, 1);

    Assert.AreEqual<bool>(false, b);

}

這裡觀察到的重點是產品類型的變化。雖然我並未為它提供任何工廠,Pex 便能建立不同類型的變化。

也請注意產生的測試包含一個判斷提示。當一個 PUT 傳回值時,Pex 會內嵌已在測試產生時間至產生的測試程式碼以傳回判斷提示的值。如此一來產生的測試通常是能夠在未來偵測重大變更,即使它們 don’t 違反程式] 程式碼中的其他判斷提示或造成層級之執行引擎例外狀況。

Pex 探索結果視窗 (請參閱 的 圖 11] 所示) 提供 Pex 所產生的單元測試的詳細資料。它也提供 Pex 建立的工廠和研究期間發生的事件的相關資訊。請注意,在 圖 11 中兩個測試失敗。Pex 會顯示針對這兩者 NullReferenceException。這可以是常見的問題您錯過出置於最後可能會導致例外狀況在生產環境中執行時的程式碼路徑的驗證檢查。

圖 11 的 Pex 探索結果

Pex 不僅會產生測試,也會分析改進的程式碼。它提供一組可以讓程式碼更穩定的建議。這些建議並非只是描述性訊息,但問題區域的實際程式碼。在按一下按鈕以 Pex 會插入到實際的來源檔案程式碼。在 [Pex 探索] 結果視窗中選取失敗的測試。右下角的按鈕會出現其標題為新增的條件。按這個按鈕,加入到來源檔案的程式碼。

這些產生的測試是正常的 MSTest 單元測試,並可以執行從 Visual Studio 測試編輯器也。如果開啟 [程式編輯器 Pex 產生的所有測試都可以使用為標準的單元測試。Pex 可以其他單元測試架構像 NUnit 和 xUnit 產生類似的測試。

Pex 也有產生涵蓋範圍報告的內建支援。這些報告提供受測程式碼周圍動態的涵蓋範圍的完整詳細資料。您可以啟用從 Pex 選項在 [工具] 功能表中的 Visual Studio 的報表,然後開啟它們,請依序按一下檢視 | Pex 功能表列之後的探索已完成的報表。

製作未來測試備妥

到目前為止您看過如何 Pex 如此便能產生與次要重整的原始程式碼的舊版程式碼的程式碼涵蓋範圍。Pex 美麗是它讓開發人員從撰寫單元測試,而且會產生它們自動,藉此減少整體的測試工作。

其中一個主要的痛苦點單元測試時維護測試套件本身。當您繼續進行專案中,您通常進行大量修改現有的程式碼。因為單元測試依存於原始碼,程式碼的任何變更會影響相對應的單元測試。它們可能會中斷,或減少程式碼涵蓋範圍。經過一段時間保持測試組合即時會變成一項挑戰。

Pex 進來在這種情況下很方便。因為 Pex 根據探勘方法,它可以搜尋的程式碼基底中的任何新變更,並建立根據它們的新測試案例。

迴歸測試的首要目的是要偵測是否修改或加入現有的程式碼的項目有影響程式碼基底有不利,透過引入的功能性 Bug 或建立新的錯誤狀況。

Pex 可以自動產生迴歸測試套件。在未來執行此迴歸測試套件時, 就會偵測到重大的變更,在程式碼失敗造成判斷提示、,會造成例外狀況,請在執行引擎 (NullReferenceException) 層級或,造成判斷提示內嵌在產生的測試失敗。每次執行全新,Pex 探索下觀察程式碼產生單元測試。在行為中的任何變更收取的 Pex 及對應的單位為它產生測試。

變更是 Inevitable

經過一段時間開發人員小組在 Fabrikam 會實現它做有 ProductId 屬性新增到 [產品類別以便如果公司將新產品新增到他們的全文檢索目錄它們可以被唯一識別的有意義。

也 Order 類別已不儲存訂單至資料存放區讓新的私用方法 SaveOrders 已新增至 Order 類別。當產品有一些庫存 Fill 方法將會呼叫這個方法。

修改過的 Fill 方法類別看起來會像這樣:

public bool Fill(Product product, int quantity) {

  if (product == null) {

    throw new ArgumentException();

  }



  if (this.orderWareHouse.HasInventory(product, quantity)) {

    this.SaveOrder(product.ProductId, quantity);



    this.orderWareHouse.Remove(product, quantity);

  return true;

  }



  return false;

}

因為未變更的 Fill 方法簽名碼,我不需要修改 [PUT。 我只是 Pex 探索再執行一次。 Pex 會執行探索,但它會產生使用利用 ProductId 也該新產品定義的輸入這一次。 它會產生 
a 全新的測試套件考慮對 Fill 方法所做的變更。 程式碼涵蓋範圍進入是 100%— 確保評估所有新的及現有的程式碼路徑。

其他單元測試來測試的 [新增] ProductId 欄位及對 Fill 方法所做的變更變異 Pex 所產生 (請參閱的 圖 12)。 這裡 PexChooseStubBehavior 設定為 Stub 後援行為 ; 而非只擲回一個 StubNotImplementedException,stubbed 的方法會呼叫 PexChoose 提供可能的傳回值。 在 Visual Studio 中執行測試,程式碼涵蓋範圍方面一次是 100%。

圖 12 的 其他 Pex 產生單元測試

[TestMethod]

[PexGeneratedBy(typeof(OrderTest))]

public void Fill12()

{

    using (PexChooseStubBehavior.NewTest())

    {

      SIWareHouse sIWareHouse;

      Order order;

      Product product;

      bool b;

      sIWareHouse = new SIWareHouse();

      order = OrderFactory.Create((IWareHouse)sIWareHouse);

      product = new Product((string)null, (string)null);

      b = this.Fill(order, product, 0);

      Assert.AreEqual<bool>(false, b);

    }

}



[TestMethod]

[PexGeneratedBy(typeof(OrderTest))]

public void Fill13()

{

    using (PexChooseStubBehavior.NewTest())

    {

      SIWareHouse sIWareHouse;

      Order order;

      Product product;

      bool b;

      sIWareHouse = new SIWareHouse();

      order = OrderFactory.Create((IWareHouse)sIWareHouse);

      product = new Product((string)null, (string)null);

      IPexChoiceRecorder choices = PexChoose.NewTest();

      choices.NextSegment(3)

          .OnCall(0, 

      "SIWareHouse.global::FabrikamSports.IWareHouse.HasInventory(Product, Int32)")

          .Returns((object)true);

      b = this.Fill(order, product, 0);

      Assert.AreEqual<bool>(true, b);

    }

}

誌謝

我想要感謝 Peli de Halleux 和 Nikolai Tillman 鼓勵我撰寫此發行項、 特殊感謝 Peli 到他 tireless 支援、 寶貴的意見和徹底檢閱。

Nikhil Sachdeva* 是 OCTO SE 小組在一個軟體開發工程 Microsoft您可以連絡他在 blogs.msdn.com/erudition 。您也可以張貼 Pex 周圍的查詢,在 social.msdn.microsoft.com/Forums/en/pex/threads 。*