RSS
热门关键字:  数据挖掘  人工智能  数据仓库  搜索引擎  数据挖掘导论

.NET环境下类测试工具的开发与实现 .

来源: 作者: 时间:2007-06-18 点击:

摘要:.NET平台为应用系统提供了良好的开发环境和技术支持,通过运用.Net框架提供的反射命名空间等技术,本文详细介绍了.NET环境下类测试工具的设计、开发全过程,该工具为测试人员提供了一个方便友好的

测试环境,对于测试工作具有一定的参考价值。

数据挖掘研究院

引言 数据挖掘研究院

测试工具是软件测试中必不可少的辅助手段,在.Net环境下微软推出了适用于 .NET Framework 领域的类测试工具nUnit,利用nUnit可以创建和执行自动的单元测试,但是nUnit只是一个单元测试框架,它需要测试人员经过一个学习过程,然后根据nUnit规范编写测试用例代码,才能最终有效地使用它。虽然对于那些公共接口不经常更改的类,这种自动化测试是有效的,然而对于那些公共接口更改很多以及包含大多数用户接口代码的类来说,这种测试的效率就十分低下了。此外,测试人员从熟悉nUnit到对给定类编写测试案例代码的过程中所耗费的工作量远远超出其手工地对该类进行一次测试所耗费的工作量,给测试工作造成一定困难。针对这些问题,本文拟开发一个通用的类测试工具,它类似于被测模块的驱动模块,在单元测试中提供方便友好的方式接受测试数据,把相关的数据转换传送给被测模块,动态调用被测模块,并输出相应的结果。 数据挖掘实验室

数据挖掘实验室

一、需求与用户界面 数据挖掘研究院

.NET框架是微软推出的用于构建、配置、运行Web服务和应用程序的多语言环境,在典型的基于.Net平台的三层应用程序中,类作为业务层的实现主体,其测试工作是软件产品开发中很重要的一个环节,也是确保软件质量的基础和关键。针对类进行单元测试需要良好的测试平台,从功能上看,类测试平台需要能够从类创建对象,让对象接收消息,并能对所接收的消息进行显示、捕捉和分析。简言之,就是对类的每个 public 方法的输入输出进行测试,检验对于给定的输入,这个类的输出是否正确。基于以上考虑,类测试程序需要完成以下任务:

数据挖掘研究院

1、动态列出类所包含的 public方法,并根据用户的选择动态列出相应方法的参数及其参数类型。

数据挖掘研究院

2、提供友好的界面供用户输入参数值,并将参数值转换成对应的参数类型传入调用方法。

数据挖掘研究院

3、动态调用类方法,并输出方法返回值及相应的错误信息描述。 数据挖掘研究院

4、输出返回的参数值,供测试人员分析。

数据挖掘研究院

根据以上对类测试工具的需求分析,下面首先进行其界面设计。类测试主要是对类方法的测试,类方法涉及的参数类型复杂多样,包括数组型参数、结构型参数、嵌套结构参数及嵌套数组参数等多种复杂结构。为了清晰的描述各参数,同时也便于测试人员操作,本测试工具软件在界面部分采用树形结构动态显示类所包含的方法及参数,其界面设计如图1。 数据挖掘研究院

图1 测试工具界面

数据挖掘研究院

界面操作过程说明如下: 数据挖掘研究院

1、测试人员进入测试界面后,首先输入待测试的程序集名称,单击“加载”按钮,动态加载程序集包含的类,并将类名称添加到下拉列表中供测试人员选择。 数据挖掘研究院

2、测试人员作出选择后,方法列表将相应类所包含的Public方法罗列出来,一旦选定待测试的类方法,则参数列表以树形结构列出该方法的输入参数类型及参数名。其中,对于结构型参数,其各数据域分别对应到当前节点的子节点;对于数组型参数,则要求测试者输入数组长度,然后在当前节点下动态增加并展示对应于数组元素的子节点。

3、测试人员选择某一参数后,将在右边栏目中列出该参数的类型及详细信息,并提供文本框供测试者输入参数值,此时测试工具程序会对输入值进行类型检查并根据情况给予友好提示。

数据挖掘研究院

4、所有参数赋值完毕后,单击“调用”按钮,动态调用方法,并将返回结果,错误描述及输出参数显示输出。

数据挖掘研究院

数据挖掘研究院

二、问题的提出与实现

数据挖掘研究院

作为类测试工具软件,为了实现以上需求,该程序有三个难点:1.如何实现动态获取类信息及动态调用类方法;2.如何实现界面的动态展示;3.如何实现结构参数、数组参数及嵌套参数等复杂参数的赋值传入及输出。针对第1个问题,在程序开发过程中通过使用.Net框架的反射技术加以解决。反射提供了封装程序集、模块和类型的对象,使用反射可以动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。然后,可以调用类型的方法或访问其字段和属性;第2个问题通过引入并灵活运用Treeview控件加以实现,由于本测试工具是具体针对一个分布式多层Web应用系统的类测试需求开发的,因此在界面部分采用Web Treeview控件,利用该控件可以方便地开发树形图,并按照树形结构组织数据;第3个问题则是在前两个问题的解决基础上通过采取递归调用的方法予以解决,下面具体阐述其实现原理及方法。 数据挖掘研究院

数据挖掘研究院

2.1 动态获取类信息及动态方法调用 数据挖掘研究院

利用.NET框架反射(System.Reflection)命名空间中的类以及System.Type类,可以加载程序集和获取在其中定义的类型信息,实现方法的动态调用。反射提供了由语言编译器(如Microsoft Visual Basic .NET)用来实现隐式晚期绑定的基础结构。System.Type类对于反射起着核心的作用,它是System.Reflection功能的根,也是访问元数据的主要方式。当反射请求加载的类型时,公共语言运行库将为它创建一个Type对象,使用Type对象的方法、字段、属性和嵌套类来查找有关该类型的所有信息。下面我们依据前面的需求分析说明代码的具体实现:代码段功能:动态加载并检测程序集,将其包含的类名称添加到列表中。 数据挖掘研究院

Dim ass As [Assembly] ‘定义程序集对象 数据挖掘研究院

ass = [Assembly].Load(Trim(assName.Text)) ‘动态加载用户指定的程序集 数据挖掘研究院

Dim ClassType As Type ‘定义Type对象

数据挖掘研究院

ClassList.Items.Add("所有待测试类") 数据挖掘实验室

For Each ClassType In ass.GetExportedTypes ‘逐一检测程序集中的Type对象

数据挖掘研究院

If ClassType.IsClass Then ‘判断是否是类对象

ClassList.Items.Add(ClassType.Name) ‘将类名称添加到列表中

数据挖掘研究院

End If 数据挖掘研究院

Next

…… 数据挖掘实验室

说明:测试工具自动检测测试类包含的全部公共方法,获取方法的详细信息

数据挖掘研究院

ClassType = GetType(TypeName) ‘获取指定类的Type对象

Dim tArrayMethodInfo As MethodInfo() ‘定义MethodInfo数组以获取类方法的信息。 数据挖掘研究院

说明:MethodInfo类用以发现方法的特性并提供对方法元数据的访问。 数据挖掘研究院

tArrayMethodInfo = ClassType.GetMethods()‘从类的Type对象获取类所包含的所有方法 数据挖掘研究院

…… 数据挖掘研究院

Dim myMethodInfo As MethodInfo 数据挖掘实验室

myMethodInfo = CType(tArrayMethodInfo(j), MethodInfo) ‘获取测试方法的MethodInfo对象

数据挖掘研究院

Dim MethodParam As ParameterInfo() ‘定义ParameterInfo数组以获取方法的参数信息

MethodParam = myMethodInfo.GetParameters ‘获取测试方法的所有参数

……

代码段功能:创建类实例,实现对测试方法的动态调用 数据挖掘研究院

Dim args() As Object ‘定义Object数组存储测试方法的参数值

数据挖掘研究院

Dim MethodObj As MethodInfo 数据挖掘研究院

Dim obj As Object

SetArgs(args, ClassType, MethodObj, MethodParam)‘设置参数值过程,参见2.3节

obj = Activator.CreateInstance(ClassType) ‘创建测试类的实例(说明:Activator类用以在本地创建对象类型,通过调用其CreateInstance方法创建类实例)。

数据挖掘研究院

retInt = MethodObj.Invoke(obj, args)‘通过Invoke方法调用由MethodInfo实例反射的方法。 数据挖掘研究院

2.2 Web界面的树形结构管理及实现

数据挖掘研究院

由于在测试过程中需要根据用户的选择动态展示类方法及其参数,对于数组,嵌套结构等复杂参数,还需要根据用户输入的长度实现数组的动态增减,因此在测试界面中主要采用树形结构管理并展示方法和参数。考虑到参数处理的复杂性及代表性,下面重点讨论“参数树”的设计实现。

数据挖掘研究院

顾名思义“参数树”是指用以展示方法参数的树结构,这里我们将参数统分为简单参数和复杂参数。简单参数是指诸如String、Int的基本类型参数,复杂参数是指数组、结构等复杂类型参数。简单参数对应于树结构的叶子节点,复杂参数对应于树结构的子节点,这些子节点下又嵌套包含子节点和叶子节点。为了便于处理,在具体实现中我们定义了"Normal"、"Enum"、"Struc"、"Arr"和"StrucArr"几种划分类型用以描述节点。当用户从方法列表中选取好方法后,即需要根据动态获取的参数信息建立相应的树结点。具体实现代码如下:

数据挖掘研究院

代码段功能:建立树结点

Private Function Node(ByVal sID As String, ByVal [text] As String, ByVal type As String) As TreeNode 数据挖掘实验室

Dim i As Integer

数据挖掘研究院

Dim sNodeType As String

Dim tNode As New TreeNode

数据挖掘研究院

Dim cssCol As New CssCollection

‘设置节点的属性值,包括节点ID、文本、节点的划分类型及节点样式等信息。

数据挖掘研究院

tNode.ID = sID 数据挖掘研究院

tNode.Text = [text]

tNode.Type = type

数据挖掘研究院

cssCol.CssText = "background:white;color:red;font-name:Arial;font-weight:bold-italic"

数据挖掘研究院

tNode.SelectedStyle = cssCol

‘对于结构型参数,动态获取结构成员信息并建立对应于结构成员的子节点。 数据挖掘研究院

If type = "Struc" Then 数据挖掘研究院

Dim f As FieldInfo() = tp.GetFields 数据挖掘研究院

For i = 0 To f.Length - 1 数据挖掘实验室

sNodeType = f(i).FieldType.Name 数据挖掘实验室

tNode.Nodes.Add(Node(I, f(i).Name, sNodeType)) 数据挖掘研究院

Next

数据挖掘研究院

End If 数据挖掘研究院

tNode.Expanded = True 数据挖掘研究院

Return tNode 数据挖掘研究院

End Function

数据挖掘研究院

2.3 参数处理

数据挖掘研究院

参数处理比较复杂,从值的传递方式划分,参数可划分为值传递型及引用传递型;从参数类型划分,参数可划分为基本类型参数,枚举型参数、结构型参数及数组参数。其中,数组参数又分为基本类型数组参数和结构数组参数。结构型参数在其结构中又可包括基本类型数组和结构数组。为了实现复杂参数的传值,我们采用递归调用的方法分别处理数组及结构参数。具体代码及说明如下:

数据挖掘研究院

代码段功能:通过“参数树”的节点信息获取参数值字符串并将其转换为相应类型的参数值。 数据挖掘研究院

Private Sub SetArgs(ByRef args() As Object, ByRef ClassType As Type, ByRef MethodObj As MethodInfo, ByRef mPara() As ParameterInfo)

数据挖掘研究院

Dim i, j, k As Integer

数据挖掘研究院

Dim sNode, Str, tStr As String

数据挖掘研究院

Dim tp As Type

mPara = MethodObj.GetParameters ‘获取方法的参数信息

数据挖掘研究院

i = mPara.Length - 1 数据挖掘研究院

If i < 0 Then ‘如果选定的方法没有参数,则args置为nothing.

args = Nothing

数据挖掘研究院

Else

数据挖掘实验室

ReDim args(i) 数据挖掘研究院

For j = 0 To i 数据挖掘研究院

sNode = TreeParams.Nodes(0).Nodes(j).Type ‘获取参数树的节点

Select Case sNode

‘对于基本类型参数和枚举型参数直接将节点数据信息转换成对应的参数类型值并赋值给参数数组。 数据挖掘研究院

Case "Normal", "Enum" 数据挖掘研究院

args(j) = TypeConvert(TreeParams.Nodes(0).Nodes(j).NodeData, mPara(j).ParameterType.Name) 数据挖掘研究院

‘对于结构型参数调用SetStrucArgs方法获取其参数对象并赋值给参数数组。

数据挖掘研究院

Case "Struc"

Dim subArgs As Object 数据挖掘研究院

SetStrucArgs(subArgs, TreeParams.Nodes(0).Nodes(j)) 数据挖掘研究院

args(j) = subArgs 数据挖掘研究院

‘对于数组型参数调用SetArrArgs方法获取其参数对象并赋值给参数数组。

Case "Arr","StrucArr"

数据挖掘研究院

Dim subArgs As Array 数据挖掘研究院

SetArrArgs(subArgs, TreeParams.Nodes(0).Nodes(j))

args(j) = subArgs 数据挖掘实验室

End Select

Next

End If 数据挖掘研究院

End Sub 数据挖掘实验室

数据挖掘实验室

代码段功能:通过“参数树”的节点信息获取参数值字符串并转换为相应类型的数组参数值。

数据挖掘研究院

Private Sub SetArrArgs(ByRef SubArgs As Array, ByRef tNode As TreeNode)

Dim Str, tStr As String 数据挖掘研究院

Dim tp As Type 数据挖掘研究院

Dim ArrLen, i As Integer

数据挖掘研究院

数据挖掘实验室

If IsNumeric(tNode.NodeData) Then

数据挖掘研究院

ArrLen = CInt(tNode.NodeData) ‘获取数组长度 数据挖掘研究院

Str = tNode.ID ‘获取表示参数类型的字符串 数据挖掘研究院

tStr = RecType(Str, tp) ‘根据字符串得到参数的划分类型及其对应的Type对象

SubArgs = Array.CreateInstance(tp, ArrLen) ‘根据获取的信息创建相应长度的数组实例 数据挖掘研究院

Select Case tStr ‘根据不同的划分类型进行不同的参数赋值处理

Case "Arr" ‘对于简单数组直接将界面获取的值信息转换成对应类型的值

For i = 0 To ArrLen - 1 数据挖掘研究院

SubArgs(i) = TypeConvert(tNode.Nodes(i).NodeData,Str) 数据挖掘研究院

Next

‘对于结构型数组依次调用SetStrucArgs方法为每一位数组成员的结构变量赋值。 数据挖掘研究院

Case "StrucArr" 数据挖掘研究院

For i = 0 To ArrLen - 1 数据挖掘实验室

Dim SubStrucArgs As Object 数据挖掘研究院

SetStrucArgs(SubStrucArgs, tNode.Nodes(i)) 数据挖掘研究院

SubArgs.SetValue(SubStrucArgs, i)

Next

数据挖掘研究院

End Select 数据挖掘研究院

Else

SubArgs = Nothing ‘如果数组长度为0,则参数对象置为nothing。

End If 数据挖掘研究院

End Sub 数据挖掘研究院

数据挖掘研究院

代码段功能:通过“参数树”的节点信息获取参数值字符串并转换为相应类型的结构参数值。 数据挖掘研究院

Private Sub SetStrucArgs(ByRef StrucArgs As Object, ByRef tNode As TreeNode) 数据挖掘研究院

Dim k As Integer

Dim sNode As String

Dim Str, tStr As String

Dim tp As Type 数据挖掘研究院

数据挖掘研究院

Str = tNode.ID ‘获取表示参数类型的字符串 数据挖掘研究院

tStr = RecType(Str, tp) ‘根据字符串得到参数的划分类型及其对应的Type对象 数据挖掘研究院

StrucArgs = Activator.CreateInstance(tp) ‘创建类实例

数据挖掘研究院

数据挖掘研究院

Dim f As FieldInfo() = tp.GetFields ‘获取结构成员信息 数据挖掘研究院

For k = 0 To f.Length - 1 数据挖掘研究院

Select Case tStr ‘根据不同的划分类型进行不同的参数赋值处理

‘对于基本类型成员直接将界面获取的值信息转换成对应类型的值 数据挖掘研究院

Case "Normal","Enum" 数据挖掘研究院

StrucSetVal(StrucArgs, f(k).Name,tNode.Nodes(k).NodeData)

‘对于数组类型(包括结构数组)成员调用SetArrArgs方法完成结构成员的赋值。 数据挖掘研究院

Case "Arr","StrucArr" 数据挖掘研究院

Dim subArrArgs As Object 数据挖掘研究院

SetArrArgs(subArrArgs, tNode.Nodes(k)) 数据挖掘实验室

StrucSetVal(StrucArgs, f(k).Name, subArrArgs) 数据挖掘研究院

‘对于结构型成员递归调用SetStrucArgs方法完成其赋值。

数据挖掘研究院

Case "Struc" 数据挖掘研究院

Dim subStrucArgs As Object 数据挖掘研究院

SetStrucArgs(subStrucArgs, tNode.Nodes(k))

StrucSetVal(StrucArgs, f(k).Name, subStrucArgs) 数据挖掘研究院

End Select 数据挖掘研究院

Next 数据挖掘研究院

End Sub 数据挖掘实验室

上述结构参数的赋值过程中运用了递归调用的处理方法,递归终止于基本类型成员的直接转换赋值。

数据挖掘研究院

至此,本文已详细阐述了测试工具开发中遇到的问题及解决方法,灵活运用这些方法及技术,对于.NET环境下的测试工作有一定的帮助及实际参考价值。

最新评论共有 0 位网友发表了评论
发表评论
评论内容:不能超过250字,需审核,请自觉遵守互联网相关政策法规。
匿名?