關於《玩轉 xUnit.Net》系列文章,我想跟你們分享的不是簡單的運行一下測試用例或是介紹一下標籤怎麼使用(這樣的文章網上不少)。上一篇《Lesson 01 玩轉 xUnit.Net 之 概述》介紹xUnit.Net的一些基本概念。從這一篇開始我將會爲你們逐一展現xUnit.Net的強大之處。仍是先看一下本文要討論的內容:html
這裏我先回顧一下前一篇文章的測試用例:git
1 using System; 2 using System.Collections.Generic; 3 using Xunit; 4 5 public class EqualExample 6 { 7 [Fact] 8 public void EqualStringIgnoreCase() 9 { 10 string expected = "TestString"; 11 string actual = "teststring"; 12 13 Assert.False(actual == expected); 14 Assert.NotEqual(expected, actual); 15 Assert.Equal(expected, actual, StringComparer.CurrentCultureIgnoreCase); 16 } 17 }
你可能已經發現,xUnit.Net的中用來標記測試方法的attribute是[Fact],而不是一個像相似[Test]這樣更傳統的標記名稱。xUnit.Net 包含了兩種主要的單元測試方式:Fact 和 Theory,這兩種方式的不一樣以下:github
首先,咱們來看一下Fact標籤的結構:數據庫
1 // Summary: 2 // Attribute that is applied to a method to indicate that it is a fact that 3 // should be run by the test runner. It can also be extended to support a customized 4 // definition of a test method. 5 [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] 6 [XunitTestCaseDiscoverer("Xunit.Sdk.FactDiscoverer", "xunit.execution.{Platform}")] 7 public class FactAttribute : Attribute 8 { 9 public FactAttribute(); 10 11 // Summary: 12 // Gets the name of the test to be used when the test is skipped. Defaults to 13 // null, which will cause the fully qualified test name to be used. 14 public virtual string DisplayName { get; set; } 15 // 16 // Summary: 17 // Marks the test so that it will not be run, and gets or sets the skip reason 18 public virtual string Skip { get; set; } 19 }
除了構造函數以外,該Attribute還提供了兩個屬性。編程
1 [Fact(DisplayName = "Lesson02.Demo01")] 2 public void Demo01_Fact_Test() 3 { 4 int num01 = 1; 5 int num02 = 2; 6 Assert.Equal<int>(3, num01 + num02); 7 } 8 9 [Fact(DisplayName = "Lesson02.Demo02", Skip = "Just test skip!")] 10 public void Demo02_Fact_Test() 11 { 12 int num01 = 1; 13 int num02 = 2; 14 Assert.Equal<int>(3, num01 + num02); 15 }
對於上面的兩個測試用例,運行結果以下。能夠看到兩個測試用例的名稱均顯示爲DisplayName對用的屬性名稱,而設置了Skip屬性的Unit Test沒有被執行。數組
關於數據驅動的測試方法,我想計算機專業出身的小夥伴應該不會陌生。這裏我但願讀者對等價類、邊界值、錯誤推測、因果圖,斷定表驅動,正交試驗設計... ...這些概念有必定的瞭解(知道是什麼就行)。簡單來講,數據驅動的測試指的是咱們的測試輸入和測試結果有着必定的關係,不一樣的輸入可能會致使輸出結果的不一樣。例如:測試登陸方法,不一樣的用戶名\密碼輸入後,會顯示不同的錯誤信息。這裏,我不想過多的討論數據驅動的測試方法應該如何設計相關的測試用例。本文的目的只要是向你們展現xUnit.Net對數據驅動的支持。安全
xUnit.Net對數據驅動測試方法的支持是經過Theory attribute實現的。你能夠用Theory替代Fact來標記你的測試方法,於此同時使用[XXXData]來提供你的輸入和輸出數據。目前[XXXData] attribute包括[InlineData]和[MemberData]。下面咱們會介紹這些 attribute的使用。app
查看Theory的源碼能夠看到,Theory是繼承自Fact的。所以,以前提到的DisplayName和Skip也一樣適用於Theory。ide
1 [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] 2 [XunitTestCaseDiscoverer("Xunit.Sdk.TheoryDiscoverer", "xunit.execution.{Platform}")] 3 public class TheoryAttribute : FactAttribute 4 { 5 public TheoryAttribute(); 6 }
Theory 和 InlineData 提供了一種簡單的數據驅動方式,代碼以下:函數
1 [Theory(DisplayName = "Lesson02.Demo03")] 2 [InlineData(1, 1, 2)] 3 [InlineData(1, 2, 3)] 4 [InlineData(2, 2, 4)] 5 public void Demo03_Theory_Test(int num01, int num02, int result) 6 { 7 Assert.Equal<int>(result, num01 + num02); 8 }
InlineData標籤的構造函數接受一個params object[] data類型的參數,值得注意的是InlineData參數的類型和數量應當與測試方法徹底匹配。在Test Explorer視圖中咱們能夠看到,該方法至關於三個測試用例,這很好的提升了測試用例的複用率和可維護性:
InlineData已經爲咱們提供了基本的數據驅動測試的能力,但同時也有幾個問題:
面對上述的狀況的時候,咱們就須要使用MemberData來完成工做。顧名思義,MemberData使用了一個當前類的某個成員來完成數據測試數據的注入,也就是用你可使用當前測試類的方法,屬性,字段進行數據的注入。是否是感受棒棒噠~~。首先,咱們來看一下MemberData的定義:
1 [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)] 2 [CLSCompliant(false)] 3 [DataDiscoverer("Xunit.Sdk.MemberDataDiscoverer", "xunit.core")] 4 public sealed class MemberDataAttribute : MemberDataAttributeBase 5 { 6 public MemberDataAttribute(string memberName, params object[] parameters); 7 8 protected override object[] ConvertDataItem(MethodInfo testMethod, object item); 9 }
[MemberData]構造函數接受兩個參數:第一,成員名稱(即方法,屬性或字段的名稱)。第二,一個參數列表(只針對方法)。例外,須要注意如下兩點:
下面咱們來看幾個具體的例子:
下面的Code中定義了屬性 InputData_Property,並在測試方法上用MemberData標記說明數據源來自對應的屬性。
1 #region MemberData InputData_Property 2 public static IEnumerable<object[]> InputData_Property 3 { 4 get 5 { 6 var driverData = new List<object[]>(); 7 driverData.Add(new object[] { 1, 1, 2 }); 8 driverData.Add(new object[] { 1, 2, 3 }); 9 driverData.Add(new object[] { 2, 3, 5 }); 10 driverData.Add(new object[] { 3, 4, 7 }); 11 driverData.Add(new object[] { 4, 5, 9 }); 12 driverData.Add(new object[] { 5, 6, 11 }); 13 return driverData; 14 } 15 } 16 17 [Theory(DisplayName = "Lesson02.Demo04")] 18 [MemberData("InputData_Property")] 19 public void Demo04_Theory_Test(int num01, int num02, int result) 20 { 21 Assert.Equal<int>(result, num01 + num02); 22 } 23 #endregion
在Test Explorer能夠看到對應的測試用例有6組:
下面的Code中定義了屬性InputData_Method,細心的同窗會發現提供數據源的方法中多了一個flag參數。這個參數的值從何而來呢?就是咱們以前說的MemberData屬性的第二個構造參數(下面代碼的21行)。
1 #region MemberData InputData_Method 2 public static IEnumerable<object[]> InputData_Method(string flag) 3 { 4 var driverData = new List<object[]>(); 5 if (flag == "Default") 6 { 7 driverData.Add(new object[] { 1, 1, 2 }); 8 driverData.Add(new object[] { 1, 2, 3 }); 9 driverData.Add(new object[] { 2, 3, 5 }); 10 } 11 else 12 { 13 driverData.Add(new object[] { 3, 4, 7 }); 14 driverData.Add(new object[] { 4, 5, 9 }); 15 driverData.Add(new object[] { 5, 6, 11 }); 16 } 17 return driverData; 18 } 19 20 [Theory(DisplayName = "Lesson02.Demo05")] 21 [MemberData("InputData_Method", "Default")] 22 //[MemberData("InputData_Method", "Other")] 23 public void Demo05_Theory_Test(int num01, int num02, int result) 24 { 25 Assert.Equal<int>(result, num01 + num02); 26 } 27 #endregion MemberData InputData_Method
此時,咱們在Test Exporer視圖中只能看見三個測試用例,如圖所示。這裏xUnit.Net爲咱們提供了根據不一樣的須要加載不一樣數據源的可能。例如:例子中的flag參數能夠是一個Excel文件名稱,參數不一樣便可讀取不一樣的文件。這裏我就不展開討論了,後續的文章會專門討論這個問題。
其實,用屬性和方法做爲數據源,已經能夠解決不少問題了。最後,咱們來看一下如何使用字段做爲數據源實現數據驅動的測試。
首先,咱們定義一個新的類型:
1 public class MatrixTheoryData<T1, T2> : TheoryData<T1, T2> 2 { 3 public MatrixTheoryData(IEnumerable<T1> data1, IEnumerable<T2> data2) 4 { 5 Contract.Assert(data1 != null && data1.Any()); 6 Contract.Assert(data2 != null && data2.Any()); 7 8 foreach (T1 t1 in data1) 9 { 10 foreach (T2 t2 in data2) 11 { 12 Add(t1, t2); 13 } 14 } 15 } 16 }
這裏用到了TheoryData類,這個類是有xUnit.Net提供。其中T1,T2表示了輸入數據的類型。也就是說這種方式是一種類型安全的輸入方式(其實,xUnit還提供了1至5個參數的TheoryData泛型)。這裏使用輸入的兩個數據集合作笛卡爾積的結果,來充當數據源。下面看一下使用的代碼:
1 #region MemberData InputData_Field 2 public static int[] Numbers = { 5, 6, 7 }; 3 public static string[] Strings = { "Hello", "world!" }; 4 public static MatrixTheoryData<string, int> MatrixData = new MatrixTheoryData<string, int>(Strings, Numbers); 5 6 [Theory(DisplayName = "Lesson02.Demo06")] 7 [MemberData("MatrixData")] 8 public void Demo06_Theory_Test(string x, int y) 9 { 10 Assert.Equal(y, x.Length); 11 } 12 #endregion MemberData InputData_Field
MatrixData字段在構造的時候就會按照規則(使用Numbers,Strings的笛卡爾積)構造對應的數據源。看一下Test Explorer視圖,此方法對應了6(3×2 = 6)個用例,用例的參數就是兩個數組的笛卡爾積的組合:
本文主要介紹了xUnit.Net的基本使用和針對數據驅動測試的支持。主要包含如下幾點:
小北De系列文章:
《[小北De編程手記] : Selenium For C# 教程》
《[小北De編程手記]:C# 進化史》(未完成)
《[小北De編程手記]:玩轉 xUnit.Net》(未完成)
Demo地址:https://github.com/DemoCnblogs/xUnit.Net