在使用xUnit.Net Framework構建單元測試或自動化測試項目的時候,不管是針對一些比較耗費資源的對象亦或是爲了支持Test case預設數據的能力,咱們都須要有一些初始化或是清理相關的動做。在xUnit.Net中,提供了多種方式來知足咱們的須要。仍是照例看一下本文要討論的內容:html
本文咱們只是討論前三個議題,其餘的議題我會在《xUnit.Net 之 Fixture(下)》中討論。git
首先,咱們虛擬一個自動化測試中很容易遇到的一個場景。有四個Test case以下圖所示:github
能夠看到,其中前三個Case(測試功能01,02,03)步驟以下,建立DB鏈接->打開瀏覽器->執行功能->關閉瀏覽器->釋放數據庫鏈接。而功能測試04僅僅是須要操做數據庫而已,沒有操做瀏覽器的需求。所以,不須要在瀏覽器中進行操做。對於這樣的一個場景,直接能想到的方法是編寫4個測試方法打上前面的[Fact]標籤,每一個方法中建立數據庫鏈接,打開瀏覽器,操做,而後釋放數據庫,關閉瀏覽器。但這樣的作法有不少的問題,好比會屢次佔用瀏覽器驅動和數據庫鏈接這樣的非託管資源。而打開數據庫鏈接和瀏覽器驅動每每是比較耗時的操做,屢次打開會無故的增長Test case的運行時間。數據庫
那麼,如何來設計測試步驟呢?這裏,我建議知足下面幾個條件(固然也是爲了講解今天的內容):編程
對於以前描述的業務場景,咱們須要在每一個Test Case執行先後,一組Test Case執行先後,全部Test Case執行先後這三個維度上添加自定義的操做。對應下來:瀏覽器
如圖所示,CollectionFixture能夠用於添加全部Test Case執行先後的一些操做(即例子中的建立和銷燬數據庫鏈接)。對於部分Case須要初始化瀏覽器,咱們可使用ClassFixture提供的功能。每一個Case執行先後的操做咱們可使用測試類構造函數和IDisposable.Dispose來進行處理。下面我就逐一爲你們講解如何使用這些功能。框架
如何在每一個Test Case執行先後作處理?這應該是每一個使用過單元測試框架的同窗都知道的。多數的框架都是經過打標籤的方式來提供相似功能的,例如:NUnit的[Setup]和[TearDown] , MSTest的[TestInitialize]和[TestCleanup]。而xUnit.Net提供了一種更加優雅的處理方式,就是利用構造函數以及IDisposable.Dispose方法來實現對應的功能。懂得一些面向對象的小夥伴也許會發現,這樣的改進主要是爲了支持依賴注入(而不是簡單的省去了標籤而已)。這也爲我後續的文章中要講到的許多功能的注入實現提供了可能。講了這麼多理論,先上一段Code:函數
1 namespace Demo.UnitTest.Lesson03_Fixture 2 { 3 public class SharedContext_Constructor : IDisposable 4 { 5 private ITestOutputHelper _output; 6 public SharedContext_Constructor(ITestOutputHelper output) 7 { 8 this._output = output; 9 _output.WriteLine("Execute constructor!"); 10 } 11 12 #region Test case 13 [Fact(DisplayName = "SharedContext.Constructor.Case01")] 14 public void TestCase01() 15 { 16 _output.WriteLine("Execute case 01!"); 17 } 18 19 [Fact(DisplayName = "SharedContext.Constructor.Case02")] 20 public void TestCase02() 21 { 22 _output.WriteLine("Execute case 02!"); 23 } 24 25 [Fact(DisplayName = "SharedContext.Constructor.Case03")] 26 public void TestCase03() 27 { 28 _output.WriteLine("Execute case 03!"); 29 } 30 #endregion 31 32 public void Dispose() 33 { 34 _output.WriteLine("Execute dispose!"); 35 } 36 } 37 }
代碼中的ITestOutputHelper就是經過構造函數注入的方式爲咱們提供了輸出Log的能力(這個下一篇的文章我會爲你們講解),這裏你只須要知道他是能夠輸出一些日誌的便可。上面的Code中,有3個Case,Case執行的時Runner會在執行每一個Case先後分別調用測試類的構造函數和對應的Dispose方法。輸出以下,咱們能夠看到測試類構造函數和Dispose方法在每個Case執行先後都被執行。post
ok,如今咱們考慮前文中提到的問題二:僅僅打開一次瀏覽器,完成測試功能01,02,03以後,再關閉瀏覽器。xUnit.Net爲咱們提供了基於類級別的Fixture,即IClassFixture。IClassFixture是一個泛型接口(標記接口,沒有任何須要實現的方法),接受一個類型。該類型的構造函數會在測試類中的第一個Test Case運行以前被調用。而其IDisposable.Dispose方法會在測試類中最後一個測試方法執行完成以後被執行。IClassFixture定義以下:單元測試
1 namespace Xunit 2 { 3 public interface IClassFixture<TFixture> where TFixture : class 4 { 5 } 6 }
如何使用IClassFixture呢?步驟以下:
Step 01 : 建立自定義的Fixture類,添加構造函數和IDisposable接口的實現方法。本文主要是講解xUnit.Net的使用,所以示例代碼中我沒有給出建立瀏覽器驅動的具體代碼(這部份內容能夠參見個人另外一個系列《[小北De編程手記] : Selenium For C# 教程》),只是添加了ExecuteCount屬性用於標記執行次數,代碼以下:
1 public class SingleBrowserFixture : IDisposable 2 { 3 public int UserId { get; set; } 4 public string UserName { get; set; } 5 public static int ExecuteCount; 6 7 public SingleBrowserFixture() 8 { 9 this.UserId = 1; 10 this.UserName = "North"; 11 ExecuteCount++; 12 13 //打開瀏覽器... 14 } 15 16 public void Dispose() 17 { 18 //關閉瀏覽器... 19 } 20 }
Step 02 :建立具體的測試類,並繼承 IClassFixture<SingleBrowserFixture>(注意:咱們用接口標記須要使用哪個類)
Step 03 :在測試類中獲取Fixture對象,xUnit.Net 用構造函數注入的方式提供了獲取IClassFixture標記對象的方法。咱們能夠在測試類的構造函數中添加對應的注入參數來獲取Fixture,這樣的設計使得咱們在測試類中全部的測試用例中共享一些Context數據,xUnit.Net執行測試用例的時候會自動識別構造參數的類型是否和IClassFixture所標記的類型是否匹配。代碼以下:
1 public class SharedContext_ClassFixture : IClassFixture<SingleBrowserFixture> 2 { 3 ITestOutputHelper _output; 4 SingleBrowserFixture _fixture; 5 static int _count; 6 public SharedContext_ClassFixture(ITestOutputHelper output, SingleBrowserFixture fixture) 7 { 8 _output = output; 9 _fixture = fixture; 10 _count++; 11 } 12 #region Test case 13 [Fact(DisplayName = "SharedContext.ClassFixture.Case01")] 14 public void TestCase01() 15 { 16 _output.WriteLine("Execute case 01! Current User:[{0}]-{1}", _fixture.UserId, _fixture.UserName); 17 _output.WriteLine("Execute count! Constructor:[{0}] , ClassFixture:[{1}]", _count, SingleBrowserFixture.ExecuteCount); 18 19 } 20 21 [Fact(DisplayName = "SharedContext.ClassFixture.Case02")] 22 public void TestCase02() 23 { 24 _output.WriteLine("Execute case 01! Current User:[{0}]-{1}", _fixture.UserId, _fixture.UserName); 25 _output.WriteLine("Execute count! Constructor:[{0}] , ClassFixture:[{1}]", _count, SingleBrowserFixture.ExecuteCount); 26 } 27 #endregion Test case 28 }
代碼中能夠看到,我用_count 標記了測試類的執行次數,用ExecuteCount標記Fixture類的執行次數,看下運行結果:
能夠看到,測試類的構造被執行了2次(也就是每一個測試用例執行的時候都會執行一次),而ClassFixture標記的測試類中的構造函數只是被執行了一次。IDisposable.Dispose 也具備相同的邏輯。
下一篇,爲你們介紹:
小北De系列文章:
《[小北De編程手記] : Selenium For C# 教程》
《[小北De編程手記]:C# 進化史》(未完成)
《[小北De編程手記]:玩轉 xUnit.Net》(未完成)
Demo地址:https://github.com/DemoCnblogs/xUnit.Net