翻看自己以前写的代码,突然间看到以前模拟的一个自动化测试架构的实现。幸好自己有写学习笔记的习惯,整理一下,贴出来,以备忘。
特性会作为元数据嵌入到程序集,通过反射机制能够得到这些元数据信息。程序员可以自己定义特性,对特性进行某种格式的定义,并利用特性来影响自己编写代码编译后的程序集(assmbly)自定义特性在编译时作为元数据被编译到程序集中,运行时通过反射机制被读取。这应该是这个自动化测试架构的立足点。
特性的应用:特性一般会在设计框架时很有用。
1、利用反射机制,作为特性的元数据可以反过来在运行时影响代码的运行配置项,(例如:Windows Form程序中[STAThread]和[MTAThread用于表明应用程序的线程模型是单线程单元还是多线程--是否可以这样归属有待商榷)或者为特殊的操作方法以特性作标记,以便在运行时做特殊处理(在P/Invoke中DLLImport特性标记方法转化为托管方法)。
2、可以用于构建管理项目程序集工具:特性表现为某种特殊注释,而注释内容可以在编译后从程序集(例如TestCaseClass.DLL)中读取出来,从而可以通过特性内容的注释和读取实现对程序集中各种类型和方法进行管理(例如可以筛选特定特性的方法执行)。
该框架定义了[ClassInitiative][ClassCleanup][TestMethod]等特性以标记测试函数,而[TestMethod]中还可以定义一些特性参数[TestProperty]去将testmethod分类。在运行时要从待测程序集(dll)中读取相应函数,并保证不同函数的运行顺序。该框架有一系列函数来完成这项工作,这些函数负责运行待测程序集中特定特性标记所标记的函数。如InvokeInitiative()运行标记有[ClassInitiative]的函数;InvokeTestMethod()运行标记[TestMethod]的函数,并用这个InvokeXXX()函数调用先后顺序保证这几种特性函数运行顺序。而这几个InvokeXXX函数中利用反射机制去筛选相应的函数去运行。其中InvokeTestMethod()应该有参数,通过主函数的开关传入,以筛选特定特性的方法。
自定义特性时要注意的
1、自定义特性继承自System.Attribute.
2、特性参数分为环境参数和命名参数,如果特性类有自己的构造函数,则参数是必选的,为命名参数;剩下的参数为命名参数。在特性中应用命名参数必须加上参数名称。可以重载构造函数以满足不同组合。
部分定义特性的代码:
namespace AttributesClass { [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] public sealed class ClassCleanupAttribute:Attribute { public ClassCleanupAttribute() { } } } namespace AttributesClass { [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] public sealed class ClassInitializeAttribute:Attribute { public ClassInitializeAttribute() { } } }
namespace AttributesClass { [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] public sealed class DescriptionAttribute:Attribute { private string description; public string Description { get { return this.description; } set { this.description = value; } } public DescriptionAttribute(string text) { this.description = text; } } }
namespace AttributesClass { [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] public sealed class PriorityAttribute:Attribute { private int priorityLevel; public int Level { get { return this.priorityLevel; } set { this.priorityLevel = value; } } public PriorityAttribute(int level) { this.priorityLevel = level; } } } namespace AttributesClass { [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] public sealed class TestPropertyAttribute:Attribute { #region Fileds private string propertyName = null; private string propertyValue = null; #endregion #region Proerties public string Name { get { return this.propertyName; } set { this.propertyName = value; } } public string Value { get { return this.propertyValue; } set { this.propertyValue = value;} } #endregion #region Constructors public TestPropertyAttribute(string strPropertyName, string strPropertyValue) { this.propertyName = strPropertyName; this.propertyValue = strPropertyValue; } #endregion } } |