关于《玩转 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