[小北De編程手記] : Lesson 04 玩轉 xUnit.Net 之 Fixture(下) [小北De編程手記] : Lesson 03 玩轉 xUnit.Net 之 Fixture(上) [小

  上一篇文章《[小北De編程手記] : Lesson 03 玩轉 xUnit.Net 之 Fixture(上)》向你們介紹了xUnit.Net 共享數據的方式、Test Case的構造函數 & IDisposable.Dispose、Class級別的Fixture : IClassFixture。這一篇,咱們接着講解後面的內容,回顧一下本文要討論的內容:html

  • xUnit.Net 共享數據的方式(上)
  • Test Case的構造函數 & IDisposable.Dispose(上)
  • Class級別的Fixture : IClassFixture(上)
  • Collection級別的Fixture : ICollectionFixture(下)
  • 依賴注入以及輸出日誌(下)

(四)Collection級別的Fixture : ICollectionFixture

  回想一下上一篇中咱們虛擬的應用場景。其中,關於問題三:「在應用程序級別統一建立數據庫鏈接,Test Case 使用的數據庫鏈接是同一份(或是統一管理的)」。 針對這一需求的實現,咱們可使用xUnit.Net的ICollectionFixture來實現。Collection級別的Fixture爲咱們提供了能夠在多個測試類之間數據共享的能力。包含在同一個Collection之下的全部測試用例共享一份上下文數據。下面咱們就來動手實現一下虛擬場景問題三之中的那個功能吧。git

  Step 01:定義CollectionFixture(Demo中的DatabaseFixturegithub

  與ClassFixture相似,自定義的CollectionFixture類,須要完成其構造函數 & IDisposable.Dispose的定義。而CollectionFixture類的構造和Dispose方法最終會在全部被標記使用該Collection的Test Class對應的Case執行先後被調用。即全部標記使用該Collection的測試方法運行以前會執行CollectionFixture的構造函數。全部標記使用該Collection的測試方法所有運行完畢以後會執行CollectionFixture的IDisposable.Dispose函數。咱們定義一個DatabaseFixture,代碼以下:數據庫

 1     public class DatabaseFixture : IDisposable
 2     {
 3         public object DatabaseContext { get; set; }
 4 
 5         public static int ExecuteCount { get; set; }
 6 
 7         public DatabaseFixture()
 8         {
 9             ExecuteCount++;
10             //初始化數據鏈接
11         }
12 
13         public void Dispose()
14         {
15             //銷燬數據鏈接
16         }
17     }

  代碼中,省略了得建立和銷燬數據庫鏈接的Code。只是使用了一個object類型的屬性來表示數據庫上下文,而且建立了一個靜態變量ExecuteCount用於標記構造函數的使用頻率。編程

  Step 02:定義Collection。app

  對於ClassFixture而言,由於是基於Class級別的數據共享。so... ... xUnit.Net提供了直接用類繼承IClassFixture接口並結合構造函數注入的方式優雅的實現了數據共享的功能。而對於Collection(一組類)的數據共享又該如何實現呢?先看一下示例代碼:框架

1     /// <summary>
2     /// 定義Collection名稱,標明使用的Fixture
3     /// </summary>
4     [CollectionDefinition("DatabaseCollection")]
5     public class DatabaseCollection : ICollectionFixture<DatabaseFixture>
6     {
7     }

  能夠看到,咱們定義了一個沒有任何內容的類DatabaseCollection,該類的主要功能是定義了一個名字爲「DatabaseCollection」(此名稱能夠和類名不一樣)的Collection,並指明該Collection所對應了Fixture。須要說明的是ICollectionFixture和IClassFixture同樣是一個泛型標記接口(即沒有任何須要實現的方法,只是用來標記對應的Fixture的類型)。而定義Collection代碼中使用了CollectionDefinition標籤,其定義以下:函數

 1 namespace Xunit
 2 {
 3     // Summary:
 4     //     Used to declare a test collection container class. The container class gives
 5     //     developers a place to attach interfaces like Xunit.IClassFixture<TFixture>
 6     //     and Xunit.ICollectionFixture<TFixture> that will be applied to all tests
 7     //     classes that are members of the test collection.
 8     [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
 9     public sealed class CollectionDefinitionAttribute : Attribute
10     {
11         // Summary:
12         //     Initializes a new instance of the Xunit.CollectionDefinitionAttribute class.
13         //
14         // Parameters:
15         //   name:
16         //     The test collection name.
17         public CollectionDefinitionAttribute(string name);
18     }
19 }

  被CollectionDefinition標記的Class在運行時會被xUnit.Net框架實例化爲一個對象,該對象將用於標記其餘的Class(有興趣的話能夠去GitHub看看xUnit.Net的源代碼)。這裏須要一個CollectionName做爲參數,該參數將會用標記那些須要使用這個CollectionFixture的類。工具

  Step 03:用Collection來標記須要使用Fixtrue的測試類。post

  xUnit.Net提供了Collection類,它的做用是用來指明測試類須要使用哪一個Collection的。全部被標記了Collection測試類中的測試方法在其運行以前會調用一次對應的CollectionFixture的構造函數,全部方法運行完畢以後會調用一次CollectionFixture的IDisposable.Dispose函數(若是定義了的話)。值得注意的是測試類中依舊是經過構造函數注入的方式獲取DatabaseFixture實例對象的。那麼,咱們來看一下Demo:

 1     [Collection("DatabaseCollection")]
 2     public class SharedContext_CollectionFixture_01
 3     {
 4         private DatabaseFixture _dbFixture;
 5         private ITestOutputHelper _output;
 6         public SharedContext_CollectionFixture_01(ITestOutputHelper output, DatabaseFixture dbFixture)
 7         {
 8             _dbFixture = dbFixture;
 9             _output = output;
10         }
11 
12         [Fact(DisplayName = "SharedContext.CollectionFixture.Case01")]
13         public void TestCase01()
14         {
15             _output.WriteLine("Execute CollectionFixture case 01!");
16             _output.WriteLine("DatabaseFixture ExecuteCount is : {0}", DatabaseFixture.ExecuteCount);
17         }
18     }
19 
20     [Collection("DatabaseCollection")]
21     public class SharedContext_CollectionFixture_02
22     {
23         private DatabaseFixture _dbFixture;
24         private ITestOutputHelper _output;
25         public SharedContext_CollectionFixture_02(DatabaseFixture dbFixture, ITestOutputHelper output)
26         {
27             _dbFixture = dbFixture;
28             _output = output;
29         }
30 
31         [Fact(DisplayName = "SharedContext.CollectionFixture.Case02")]
32         public void TestCase01()
33         {
34             _output.WriteLine("Execute CollectionFixture case 02!");
35             _output.WriteLine("DatabaseFixture ExecuteCount is : {0}", DatabaseFixture.ExecuteCount);
36         }
37     }

  Dome中定義了兩個測試類,每一個測試類中有一個測試方法,並用Collection指明瞭須要使用的Collection的名稱。運行結果以下:

  能夠看到,DatabaseFixture中的構造函數只是被執行了一次(IDisposable.Dispose也有相同的邏輯)。所以,實際的單元測試中,咱們能夠此處構建、管理數據庫鏈接以節省資源的開銷。

(五)依賴注入以及輸出日誌

  依賴注入是一個重要的OOP的法則,用來削減計算機程序的耦合問題。現在已成爲許多不一樣領域軟件框架的核心。關於依賴注入的概念,我想你們都不會陌生。這裏我列出了幾種依賴注入的主要方式:

  • 類型1 (基於接口): 可服務的對象須要實現一個專門的接口,該接口提供了一個對象,能夠重用這個對象查找依賴(其它服務)。
  • 類型2 (基於setter): 經過JavaBean的屬性(setter方法)爲可服務對象指定服務。
  • 類型3 (基於構造函數): 經過構造函數的參數爲可服務對象指定服務。

   這裏談到依賴注入,主要是想跟你們分享本人對xUnit.Net的設計理念的一點點理解。我在第一篇xUnit.Net系列文章《[小北De編程手記] : Lesson 01 玩轉 xUnit.Net 之 概述》中曾提到過:xUnit.Net的一個改進就是在處理每一個Test Case的初始化和清理方法時再也不使用屬性標籤來標記,而是採用了構造函數和IDisposable.Dispose方法。這樣作的一個直接好處就是使得依賴注入更容易的運用於xUnit.Net之中。前面例子中各個級別的Fixture,日誌對象... ...都是經過依賴注入的方式簡單,優雅的被咱們所獲取到。而對於日誌對象,使用者也無需去關注它會輸出到哪裏(這個是由運行Case的工具<即Runner>決定),咱們甚至不用關心它是如何被實例化。當使用不一樣的Runner運行Case時,Runner會針對xUnit.net的接口去實現一套屬於本身的輸出方式。下面咱們來回顧一下輸出接口以及它的使用方式:

1 namespace Xunit.Abstractions
2 {
3     public interface ITestOutputHelper
4     {
5         void WriteLine(string message);
6         void WriteLine(string format, params object[] args);
7     }
8 }
  能夠看到,ITestOutputHelper定義了兩個輸出方法,使用者能夠經過下面的方式(構造函數注入)獲取到運行時Runner提供的輸出對象。而關於對象的實例化,管理等操做都是由運行Case的Runner(程序)來管理的。後面我會爲你們講解如何自定義Runner以及自定義Runner的意義所在,這裏就再也不贅述了。
 1     public class SharedContext_ClassFixture : IClassFixture<SingleBrowserFixture>
 2     {
 3         ITestOutputHelper _output;
 4         public SharedContext_ClassFixture(ITestOutputHelper output , SingleBrowserFixture fixture)
 5         {
 6             _output = output;
 7         }
 8         #region Test case
 9         [Fact(DisplayName = "SharedContext.ClassFixture.Case01")]
10         public void TestCase01()
11         {
12             _output.WriteLine("Log here");
13         }
14         #endregion Test case
15     }

  日誌對象自己的使用很簡單,單獨拉出來說是爲了向你們展現xUnit.Net設計的工匠精神(更靠近設計者的意圖)。不少框架級別的改變雖小(NUnit使用屬性標籤標記初始化方法,而xUnit.Net使用構造函數),可是用意頗深。so... ... 咱們就慢慢體會吧~~~

這兩篇文章主要和你們探討了如下問題:

  • xUnit.Net 共享數據的方式
  • Test Case的構造函數 & IDisposable.Dispose
  • Class級別的Fixture : IClassFixture
  • Collection級別的Fixture : ICollectionFixture
  • 依賴注入以及輸出日誌

  關於的xUnit.Net Fixture的基本使用就先介紹到這裏了,下一篇爲你們講解一下如何在Fixture的層面上擴展xUnit.Net的功能。到時候,讓咱們一塊兒來看看xUnit.Net在可擴展性方面有何過人之處?

 

小北De系列文章:

  《[小北De編程手記] : Selenium For C# 教程

  《[小北De編程手記]:C# 進化史》(未完成)

  《[小北De編程手記]:玩轉 xUnit.Net》(未完成)

Demo地址:https://github.com/DemoCnblogs/xUnit.Net

若是您認爲這篇文章還不錯或者有所收穫,能夠點擊右下角的 【推薦】按鈕,由於你的支持是我繼續寫做,分享的最大動力!
做者:小北@North
來源:http://www.cnblogs.com/NorthAlan
聲明:本博客原創文字只表明本人工做中在某一時間內總結的觀點或結論,與本人所在單位沒有直接利益關係。非商業,未受權,貼子請以現狀保留,轉載時必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接。
相關文章
相關標籤/搜索