[小北De编程手记] : Lesson 03 玩转 xUnit.Net 之 Fixture(上) [小北De编程手记] : Selenium For C# 教程 [小北De编程手记] : Seleni

  在使用xUnit.Net Framework构建单元测试或自动化测试项目的时候,不管是针对一些比较耗费资源的对象亦或是为了支持Test case预设数据的能力,咱们都须要有一些初始化或是清理相关的动做。在xUnit.Net中,提供了多种方式来知足咱们的须要。仍是照例看一下本文要讨论的内容:html

  • xUnit.Net 共享数据的方式(上)
  • Test Case的构造函数 & IDisposable.Dispose(上)
  • Class级别的Fixture : IClassFixture(上)
  • Collection级别的Fixture : ICollectionFixture(下)
  • 依赖注入以及输出日志(下)

  本文咱们只是讨论前三个议题,其余的议题我会在《xUnit.Net 之 Fixture(下)》中讨论。git

  首先,咱们虚拟一个自动化测试中很容易遇到的一个场景。有四个Test case以下图所示:github

  

  能够看到,其中前三个Case(测试功能01,02,03)步骤以下,建立DB链接->打开浏览器->执行功能->关闭浏览器->释放数据库链接。而功能测试04仅仅是须要操做数据库而已,没有操做浏览器的需求。所以,不须要在浏览器中进行操做。对于这样的一个场景,直接能想到的方法是编写4个测试方法打上前面的[Fact]标签,每一个方法中建立数据库链接,打开浏览器,操做,而后释放数据库,关闭浏览器。但这样的作法有不少的问题,好比会屡次占用浏览器驱动和数据库链接这样的非托管资源。而打开数据库链接和浏览器驱动每每是比较耗时的操做,屡次打开会无故的增长Test case的运行时间。数据库

  那么,如何来设计测试步骤呢?这里,我建议知足下面几个条件(固然也是为了讲解今天的内容):编程

  • 在每一个测试任务开始以前作一些数据的初始化工做。
  • 打开一次浏览器,完成测试功能01,02,03以后,在关闭浏览器。在减小建立浏览器的开销的同时节省了测试时间
  • 在应用程序级别统一建立数据库链接,Test Case 使用的数据库链接是同一份(或是统一管理的)。

(一)xUnit.Net 共享数据的方式

  对于以前描述的业务场景,咱们须要在每一个Test Case执行先后,一组Test Case执行先后,全部Test Case执行先后这三个维度上添加自定义的操做。对应下来:浏览器

  • 每一个Test Case执行先后 : 在每一个测试任务开始以前作一些数据的初始化工做。
  • 一组Test Case执行先后 : 打开一次浏览器,完成测试功能01,02,03以后,再关闭浏览器。
  • 全部Test Case执行先后 : 在应用程序级别统一建立数据库链接。

  如图所示,CollectionFixture能够用于添加全部Test Case执行先后的一些操做(即例子中的建立和销毁数据库链接)。对于部分Case须要初始化浏览器,咱们可使用ClassFixture提供的功能。每一个Case执行先后的操做咱们可使用测试类构造函数和IDisposable.Dispose来进行处理。下面我就逐一为你们讲解如何使用这些功能。框架

(二)Test 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

(三)Class级别的Fixture : IClassFixture

  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 也具备相同的逻辑。

  下一篇,为你们介绍:

  • Collection级别的Fixture : ICollectionFixture(下)
  • 依赖注入以及输出日志(下)

小北De系列文章:

  《[小北De编程手记] : Selenium For C# 教程

  《[小北De编程手记]:C# 进化史》(未完成)

  《[小北De编程手记]:玩转 xUnit.Net》(未完成)

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

若是您认为这篇文章还不错或者有所收获,能够点击右下角的 【推荐】按钮,由于你的支持是我继续写做,分享的最大动力!
做者:小北@North
来源:http://www.cnblogs.com/NorthAlan
声明:本博客原创文字只表明本人工做中在某一时间内总结的观点或结论,与本人所在单位没有直接利益关系。非商业,未受权,贴子请以现状保留,转载时必须保留此段声明,且在文章页面明显位置给出原文链接。
相关文章
相关标签/搜索