[小北De编程手记] : Lesson 04 玩转 xUnit.Net 之 Fixture(下)
2016-02-22 10:39
555 查看
上一篇文章《[小北De编程手记] : Lesson 03 玩转 xUnit.Net 之 Fixture(上)》向大家介绍了xUnit.Net 共享数据的方式、Test Case的构造函数 & IDisposable.Dispose、Class级别的Fixture : IClassFixture。这一篇,我们接着讲解后面的内容,回顾一下本文要讨论的内容:
xUnit.Net 共享数据的方式(上)
Test Case的构造函数 & IDisposable.Dispose(上)
Class级别的Fixture : IClassFixture(上)
Collection级别的Fixture : ICollectionFixture(下)
依赖注入以及输出日志(下)
Step 01:定义CollectionFixture(D[b]emo中的DatabaseFixture)[/b]
与ClassFixture类似,自定义的CollectionFixture类,需要完成其构造函数 & IDisposable.Dispose的定义。而CollectionFixture类的构造和Dispose方法最终会在所有被标记使用该Collection的Test Class对应的Case执行前后被调用。即所有标记使用该Collection的测试方法运行之前会执行CollectionFixture的构造函数。所有标记使用该Collection的测试方法全部运行完毕之后会执行CollectionFixture的IDisposable.Dispose函数。我们定义一个DatabaseFixture,代码如下:
代码中,省略了得创建和销毁数据库连接的Code。只是使用了一个object类型的属性来表示数据库上下文,并且创建了一个静态变量ExecuteCount用于标记构造函数的使用频率。
Step 02:定义Collection。
对于ClassFixture而言,因为是基于Class级别的数据共享。so... ... xUnit.Net提供了直接用类继承IClassFixture接口并结合构造函数注入的方式优雅的实现了数据共享的功能。而对于Collection(一组类)的数据共享又该如何实现呢?先看一下示例代码:
可以看到,我们定义了一个没有任何内容的类DatabaseCollection,该类的主要功能是定义了一个名字为“DatabaseCollection”(此名称可以和类名不同)的Collection,并指明该Collection所对应了Fixture。需要说明的是ICollectionFixture和IClassFixture一样是一个泛型标记接口(即没有任何需要实现的方法,只是用来标记对应的Fixture的类型)。而定义Collection代码中使用了CollectionDefinition标签,其定义如下:
被CollectionDefinition标记的Class在运行时会被xUnit.Net框架实例化为一个对象,该对象将用于标记其他的Class(有兴趣的话可以去GitHub看看xUnit.Net的源代码)。这里需要一个CollectionName作为参数,该参数将会用标记那些需要使用这个CollectionFixture的类。
Step 03:用Collection来标记需要使用Fixtrue的测试类。
xUnit.Net提供了Collection类,它的作用是用来指明测试类需要使用哪个Collection的。所有被标记了Collection测试类中的测试方法在其运行之前会调用一次对应的CollectionFixture的构造函数,所有方法运行完毕之后会调用一次CollectionFixture的IDisposable.Dispose函数(如果定义了的话)。值得注意的是测试类中依旧是通过构造函数注入的方式获取DatabaseFixture实例对象的。那么,我们来看一下Demo:
Dome中定义了两个测试类,每个测试类中有一个测试方法,并用Collection指明了需要使用的Collection的名称。运行结果如下:
可以看到,DatabaseFixture中的构造函数只是被执行了一次(IDisposable.Dispose也有相同的逻辑)。因此,实际的单元测试中,我们可以此处构建、管理数据库连接以节省资源的开销。
类型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的接口去实现一套属于自己的输出方式。下面我们来回顾一下输出接口以及它的使用方式:
日志对象本身的使用很简单,单独拉出来讲是为了向大家展示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
如果您认为这篇文章还不错或者有所收获,可以点击右下角的[b]【推荐】按钮,因为你的支持是我继续写作,分享的最大动力![/b]
作者:小北@North
来源:http://www.cnblogs.com/NorthAlan
声明:本博客原创文字只代表本人工作中在某一时间内总结的观点或结论,与本人所在单位没有直接利益关系。非商业,未授权,贴子请以现状保留,转载时必须保留此段声明,且在文章页面明显位置给出原文连接。
xUnit.Net 共享数据的方式(上)
Test Case的构造函数 & IDisposable.Dispose(上)
Class级别的Fixture : IClassFixture(上)
Collection级别的Fixture : ICollectionFixture(下)
依赖注入以及输出日志(下)
(四)Collection级别的Fixture : ICollectionFixture
回想一下上一篇中我们虚拟的应用场景。其中,关于问题三:“在应用程序级别统一创建数据库连接,Test Case 使用的数据库连接是同一份(或是统一管理的)”。 针对这一需求的实现,我们可以使用xUnit.Net的ICollectionFixture来实现。Collection级别的Fixture为我们提供了可以在多个测试类之间数据共享的能力。包含在同一个Collection之下的所有测试用例共享一份上下文数据。下面我们就来动手实现一下虚拟场景问题三之中的那个功能吧。Step 01:定义CollectionFixture(D[b]emo中的DatabaseFixture)[/b]
与ClassFixture类似,自定义的CollectionFixture类,需要完成其构造函数 & IDisposable.Dispose的定义。而CollectionFixture类的构造和Dispose方法最终会在所有被标记使用该Collection的Test Class对应的Case执行前后被调用。即所有标记使用该Collection的测试方法运行之前会执行CollectionFixture的构造函数。所有标记使用该Collection的测试方法全部运行完毕之后会执行CollectionFixture的IDisposable.Dispose函数。我们定义一个DatabaseFixture,代码如下:
public class DatabaseFixture : IDisposable { public object DatabaseContext { get; set; } public static int ExecuteCount { get; set; } public DatabaseFixture() { ExecuteCount++; //初始化数据连接 } public void Dispose() { //销毁数据连接 } }
代码中,省略了得创建和销毁数据库连接的Code。只是使用了一个object类型的属性来表示数据库上下文,并且创建了一个静态变量ExecuteCount用于标记构造函数的使用频率。
Step 02:定义Collection。
对于ClassFixture而言,因为是基于Class级别的数据共享。so... ... xUnit.Net提供了直接用类继承IClassFixture接口并结合构造函数注入的方式优雅的实现了数据共享的功能。而对于Collection(一组类)的数据共享又该如何实现呢?先看一下示例代码:
/// <summary> /// 定义Collection名称,标明使用的Fixture /// </summary> [CollectionDefinition("DatabaseCollection")] public class DatabaseCollection : ICollectionFixture<DatabaseFixture> { }
可以看到,我们定义了一个没有任何内容的类DatabaseCollection,该类的主要功能是定义了一个名字为“DatabaseCollection”(此名称可以和类名不同)的Collection,并指明该Collection所对应了Fixture。需要说明的是ICollectionFixture和IClassFixture一样是一个泛型标记接口(即没有任何需要实现的方法,只是用来标记对应的Fixture的类型)。而定义Collection代码中使用了CollectionDefinition标签,其定义如下:
namespace Xunit { // Summary: // Used to declare a test collection container class. The container class gives // developers a place to attach interfaces like Xunit.IClassFixture<TFixture> // and Xunit.ICollectionFixture<TFixture> that will be applied to all tests // classes that are members of the test collection. [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] public sealed class CollectionDefinitionAttribute : Attribute { // Summary: // Initializes a new instance of the Xunit.CollectionDefinitionAttribute class. // // Parameters: // name: // The test collection name. public CollectionDefinitionAttribute(string name); } }
被CollectionDefinition标记的Class在运行时会被xUnit.Net框架实例化为一个对象,该对象将用于标记其他的Class(有兴趣的话可以去GitHub看看xUnit.Net的源代码)。这里需要一个CollectionName作为参数,该参数将会用标记那些需要使用这个CollectionFixture的类。
Step 03:用Collection来标记需要使用Fixtrue的测试类。
xUnit.Net提供了Collection类,它的作用是用来指明测试类需要使用哪个Collection的。所有被标记了Collection测试类中的测试方法在其运行之前会调用一次对应的CollectionFixture的构造函数,所有方法运行完毕之后会调用一次CollectionFixture的IDisposable.Dispose函数(如果定义了的话)。值得注意的是测试类中依旧是通过构造函数注入的方式获取DatabaseFixture实例对象的。那么,我们来看一下Demo:
[Collection("DatabaseCollection")] public class SharedContext_CollectionFixture_01 { private DatabaseFixture _dbFixture; private ITestOutputHelper _output; public SharedContext_CollectionFixture_01(ITestOutputHelper output, DatabaseFixture dbFixture) { _dbFixture = dbFixture; _output = output; } [Fact(DisplayName = "SharedContext.CollectionFixture.Case01")] public void TestCase01() { _output.WriteLine("Execute CollectionFixture case 01!"); _output.WriteLine("DatabaseFixture ExecuteCount is : {0}", DatabaseFixture.ExecuteCount); } } [Collection("DatabaseCollection")] public class SharedContext_CollectionFixture_02 { private DatabaseFixture _dbFixture; private ITestOutputHelper _output; public SharedContext_CollectionFixture_02(DatabaseFixture dbFixture, ITestOutputHelper output) { _dbFixture = dbFixture; _output = output; } [Fact(DisplayName = "SharedContext.CollectionFixture.Case02")] public void TestCase01() { _output.WriteLine("Execute CollectionFixture case 02!"); _output.WriteLine("DatabaseFixture ExecuteCount is : {0}", DatabaseFixture.ExecuteCount); } }
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的接口去实现一套属于自己的输出方式。下面我们来回顾一下输出接口以及它的使用方式:
namespace Xunit.Abstractions { public interface ITestOutputHelper { void WriteLine(string message); void WriteLine(string format, params object[] args); } }
可以看到,ITestOutputHelper定义了两个输出方法,使用者可以通过下面的方式(构造函数注入)获取到运行时Runner提供的输出对象。而关于对象的实例化,管理等操作都是由运行Case的Runner(程序)来管理的。后面我会为大家讲解如何自定义Runner以及自定义Runner的意义所在,这里就不再赘述了。
public class SharedContext_ClassFixture : IClassFixture<SingleBrowserFixture> { ITestOutputHelper _output; public SharedContext_ClassFixture(ITestOutputHelper output , SingleBrowserFixture fixture) { _output = output; } #region Test case [Fact(DisplayName = "SharedContext.ClassFixture.Case01")] public void TestCase01() { _output.WriteLine("Log here"); } #endregion Test case }
日志对象本身的使用很简单,单独拉出来讲是为了向大家展示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
如果您认为这篇文章还不错或者有所收获,可以点击右下角的[b]【推荐】按钮,因为你的支持是我继续写作,分享的最大动力![/b]
作者:小北@North
来源:http://www.cnblogs.com/NorthAlan
声明:本博客原创文字只代表本人工作中在某一时间内总结的观点或结论,与本人所在单位没有直接利益关系。非商业,未授权,贴子请以现状保留,转载时必须保留此段声明,且在文章页面明显位置给出原文连接。
相关文章推荐
- java 常见的2种单例模式
- java中Date无法获取数据库时分秒的问题
- C++学习笔记(十二):重载函数
- PHP常用的文件操作函数集锦
- java程序实现短信发送(可调用免费短信接口)
- IE 实现代码示例
- 如何利用开发开具调试程序-VC++6.0篇
- php函数
- java-web总结--jsp--会话
- php判断是否为手机站
- 加载properties文件属性的Java工具类实现
- C语言的预处理
- java读取excel文件并写入另一个excel文件
- Socket编程
- c语言中的结构体
- C++ | boost库 类的序列化
- phpstorm10.0.3破解版安装教程及汉化方法
- Eclipse的Jar包解压出System.js里String与Boolean定义分号可有可无吗?
- java的zip解压
- selenium入门第一个脚本(python篇)