时间有点晚了,我这里就直接拿出几个最关键的一方
第一,将对象库转换成XML文件,并从中提取出对象字符串,我们输入 的可能仅仅是按钮名称,但需要从中取得对象的整个父对象。
TestDllFilePath=FrameworkPath+"Test_DotNetDll\QTPBuildObjectLayer.dll" Set FileOperation= DotNetFactory.CreateInstance("System.IO.File") Set DirectoryOperation= DotNetFactory.CreateInstance("System.IO.Directory") Set ObjectBuild= DotNetFactory.CreateInstance("QTPBuildObjectLayer.BuildObject",TestDllFilePath) Set XMLFileExport=CreateObject("Mercury.ObjectRepositoryUtil") Set TestDataDictionary=CreateObject("Scripting.Dictionary") 这里是前提,需要用不对劲这些外部dll。或者通过dotnetfactory创建,直接高用基于.net的方法
第二,处理对象库及转换成XML.
'FunctionName:BuildObject 'Description:convert object name to string, eg. convert "Login" to window("Main").Winbutton("Login"), description program is supported 'FunctionType:Function Utility , referenced by ActionEntry Function BuildObject(ObjectName) If left(ObjectName,3)="Dsc" Then BuildObject=Dsc_Object(ObjectName) 'reserved Else RepositoriesNumTemp=1 For RepositoriesNumTemp=1 to RepositoriesCollection.Count RepositoryExportToXMLFile RepositoriesNumTemp ObjectFound = ConvertToObjectLayer(ObjectName) If ObjectFound <> "" Then BuildObject = ObjectFound Exit for End If Next End If End Function 'FunctionName:LoadRepositories 'Description:if Repositories_Name give a repository,then add it to current actions,else 'FunctionType: Function Utility,referenced by ActionEntry funcion Sub LoadRepositories(RepositoriesName) TestRepositoriesFilePath=TestRepositoriesPath+RepositoriesName If RepositoriesCollection.Find(TestRepositoriesFilePath)=False Then RepositoriesCollection.Add(TestRepositoriesFilePath) else RepositoriesCollection.Remove(TestRepositoriesFilePath) RepositoriesCollection.Add(TestRepositoriesFilePath) end if End Sub 'FunctionName:RepositoryExportToXMLFile 'Description :Export all repositorys to xml files. 'Function Type:Utility Function,referenced by build Object Sub RepositoryExportToXMLFile(RepositoryIndex) If FileOperation.Exists(TempXMLFilePath) Then FileOPeration.Delete(TempXMLFilePath) End If XMLFileExport.ExportToXML (FrameworkFolderPath+RepositoriesCollection.Item(RepositoryIndex)),TempXMLFilePath End Sub 'FuncitonName:ConvertToObjectLayer 'Description:Find object name in temp.xml file and recombine to a string like windows("test").winbutton("hello") 'Function:Function Utilities ,referenced by BuildObject Function ConvertToObjectLayer(ObjectName) ObjectArray=ObjectBuild.FilePathObjectName(TempXMLFilePath,ObjectName) If ObjectArray<>"" Then ObjectArray=Split(ObjectArray,"@") ConvertToObjectLayer=ObjectArray(Ubound(ObjectArray)) ELSE ConvertToObjectLayer="" End If End Function
上面可以 看到高有季buildobjeectt方法 ,这个其实是net写的dll.具体 代码如下,这里千万不能如错,为了考虑以后兼容,我在这里返回的是数组。原则QTP对象库中是不要出现两个同名的对象。这个地方是千万不能出错的。为了简单和效率就用了linq to xml.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Xml; using System.Xml.XPath; using System.Xml.Xsl; using System.Xml.Linq; namespace QTPBuildObjectLayer { public class BuildObject { XNamespace NameSpace = "http://www.mercury.com/qtp/ObjectRepository"; public string FilePathObjectName(string FilePath, string LogicName, string ObjectType = "") { string ParentNode = ""; string ChildNode = ""; string BuildObjectString = ""; String ObjectList = ""; try { if (FilePath == null || LogicName == null) { ObjectList = "Please input right FilePath or LogicName"; return ObjectList.ToString(); } XDocument test = XDocument.Load(FilePath); var Query = from c in test.Descendants(NameSpace + "Object") where ((ObjectType == "") ? (c.Attribute("Name").Value == LogicName) : (c.Attribute("Name").Value == LogicName && c.Attribute("Class").Value == ObjectType)) select new { IsChildObject = c.Parent.Name.Equals(NameSpace + "ChildObjects"), LogicNameClass = c.Attribute("Class").Value //ChildObjectsElementName = c.Attribute("Name").Parent.Parent.Name, //ParentAttributionName = c.Attribute("Name").Parent.Parent.Parent.Attribute("Name").Value, //ParentAttributionValue = c.Attribute("Name").Parent.Parent.Parent.Attribute("Class").Value }; foreach (var Item in Query) { ChildNode = ""; ChildNode = Item.LogicNameClass + "(\"" + LogicName + "\")"; if (Item.IsChildObject) { ParentNode = ParentNode + BuildParentNode(FilePath, LogicName, Item.LogicNameClass); } BuildObjectString = ParentNode + ChildNode; ObjectList = ObjectList + "@" + (BuildObjectString); } return ObjectList; } catch (Exception exception) { ObjectList = exception.ToString(); return ObjectList; } } public string BuildParentNode(String FilePath, string LogicName, String LogicNameClass) { string ParentNode = ""; XDocument ParentObject = XDocument.Load(FilePath); try { var Query1 = from b in ParentObject.Descendants(NameSpace + "Object") where (b.Attribute("Name").Value == LogicName) && (b.Attribute("Class").Value == LogicNameClass) select new { ParentIsChildObject = b.Parent.Parent.Name.Equals(NameSpace + "ChildObjects"), ParentAttributionName = b.Parent.Parent.Attribute("Name").Value, ParentAttributionClass = b.Parent.Parent.Attribute("Class").Value }; foreach (var Item1 in Query1) { ParentNode = Item1.ParentAttributionClass.ToString() + "(\"" + Item1.ParentAttributionName + "\")."; if (Item1.ParentIsChildObject) { LogicName = Item1.ParentAttributionName; LogicNameClass = Item1.ParentAttributionClass; ParentNode = ParentNode + BuildParentNode(FilePath, LogicName, LogicNameClass); } // break; } return ParentNode; } catch (Exception) { return "BuildChildObject Failed"; } } } }
下面也是比较关键的,写了一大堆 东西就是为了实现 一个功能,把转换后的对象字符串、测试数据与对应的事件关联起来,即将读取到的window("aa").edit("b") 及111111 set拼接在一起执行,然后找到对应的方法并执行,这里每一个set.click.doubleclick都是作为 action部分的,每个事件一个方法,如果执行失败就截图并记录即可。
'FunctionName: ExecuteActionSteps 'Description: Execute all actions from this function 'FunctionType:Framework core partion, referenced by Utility Function library,ActionEntry function Function ExecuteActionStpes(strKeyWords,strObjectName,strParameter,strRepositoriesName) If strKeyWords="" Then Reporter.ReportEvent micFail,"Function ActionEntry","Business_Function is null" Exit function End If Environment("DefinedActionName")=strKeyWords If strRepositoriesName <> "" Then LoadRepositories RepositoriesName End If If strObjectName <>"" Then If ucase(left(strObjectName,4))="DSC_" Then strObject=DespritionObject(strObjectName) else strObject=BuildObject(strObjectName) End if If strObject="" OR strObject="Not Found" Then Reporter.ReportEvent micFail,"ObjectString:"+strObjectName,"Build object failed,please check your entry or object is described correctly" Exit function End If If strParameter = "" Then Eval(Trim(strKeyWords)+"("+strObject+")") else TestParameter=ReOrganizeParameter(strParameter) Eval(Trim(strKeyWords)+"("+strObject+","+TestParameter+")") End If Else If strObjectName="" and strParameter="" Then Eval(Trim(strKeyWords)) else If strObjectName="" Then TestParameter=ReOrganizeParameter(strParameter) Eval(Trim(strKeyWords)+"("+TestParameter+")") End If End If End If End Function
根据eval的结果,自动跳转到action方法部分。以下仅为一个,示例,所以有的均可参照这种方法。其它的类似就不一一重复
'FunctionName:Enter_Value_In_Field 'Descriptions:Enter vlaue in edit box or text box etc. 'Input Parameter:Object String,input parameter and if value is encryped flag 'FunctionType: Framework core functions, referenced by execute action steps Function Enter_Value_In_Field(strObject,strParameter,IsEncryped) Enter_Value_In_Field="FAIL" If strObject.exist(10) Then strObject.set "" If Ucase(IsEncryped)="YES" Then EncypParameter=Crypt.Encrypt(strParameter) strObject.SetSecure EncypParameter else strObject.set strParameter End If Enter_Value_In_Field="PASS" call LogAndCaptureImage(strObject,"Enter_Value_In_Field","PASS","Object:"+strObject.Tostring+" executed successfully") else Enter_Value_In_Field="FAIL" call LogAndCaptureImage(strObject,"Enter_Value_In_Field","FAIL","Object:"+strObject.Tostring+" Not Found") End If End Function
最后就是执行的结果需要进行截图,并记录运行每一步的结果
'FunctionName:LogAndCaptureImage 'Descriptions:If Function parameter contain object name, log action execute status and capture img ,if object is null,log execute status 'Input Parameter:strObjectName,ReportName,ReportStatus,ReportDetails 'Output Parameter:None 'FunctionType:Global function, referenced by all actions Function LogAndCaptureImage(ObjectName,ActionStep,ReportStatus,ReportDetails) ImageFilePath=Environment("FrameworkPath")+"Test_Result\"+Environment("DefinedTestName")+"\"+cstr(Environment("DefinedTestIteration"))+"_Image" FormattedCurrentDateTime=year(Date)&month(date)&day(date)&hour(now)&minute(now)&second(now) ReportStep=ActionStep ImageFileFullPath="" If Not IsEmpty(ObjectName) and IsObject(ObjectName) Then ReserveObject=split(trim(Replace(ObjectName.Tostring,"["," "))," ") ReserveObjectName=ReserveObject(Lbound(split(ObjectName.Tostring," "))) ObjectString=BuildObject(ReserveObjectName) If Ubound(split(ObjectString,")."))>0 Then TempArray=split(ObjectString,").") TempObject="set ObjectToCapture="+TempArray(0)+")" ELSE TempObject="set ObjectToCapture="+ObjectString End If Execute TempObject If DirectoryOperation.exists(ImageFilePath) = "False" Then DirectoryOperation.CreateDirectory(ImageFilePath) End If ImageFileFullPath=ImageFilePath+"\"+FormattedCurrentDateTime+".png" If ObjectToCapture.exist(5) and Environment("ImageCaptureLevel") <> "FailedOnly" Then ObjectToCapture.CaptureBitmap ImageFileFullPath,true ObjectToCapture.CaptureBitmap ImageFileFullPath,true If ReportStatus="PASS" Then If Environment("ImageCaptureLevel") <> "FailedOnly" Then Reporter.ReportEvent micPass,ReportStep,ObjectName.Tostring+"Finished", ImageFileFullPath else 'Reporter.ReportEvent micPass,ReportStep,ReportDetails+": "+"<img src='"+ImageFilePath+"\"+FormattedCurrentDateTime+".png"+"' />" Reporter.ReportEvent micPass,ReportStep,ReportDetails End if call GenerateReport(ReportStep,"PASSED",ReportDeatils,ImageFileFullPath) else Reporter.ReportEvent micFail,ReportStep,ReportDetails,ImageFileFullPath call GenerateReport(ReportStep,"FAILED",ReportDeatils,ImageFileFullPath) End If Else If ReportStatus="PASS" Then Reporter.ReportEvent micPass,ReportStep,ReportDetails call GenerateReport(ReportStep,"PASSED",ReportDeatils,"") else Reporter.ReportEvent micFail,ReportStep,ReportDetails call GenerateReport(ReportStep,"FAILED",ReportDeatils,"") End If End if End If End Function
最后一步是压缩成ZIP文件将结果发送至指定邮箱。这个功能多上有很多,这里就不详细介绍了。
'FunctionName:ZipFile 'Description:Zip a file to *.ZIP 'InputParameter:Source file path,desination file path 'FunctionType:Framework core function Function ZipFile(SourceFolder,ZipFilePath) Set Fso=createobject("Scripting.FileSystemObject") for each files in Fso.GetFolder(SourceFolder).Files If lcase(right(files,4))=".zip" Then Fso.DeleteFile files.path,True End If Next set File=Fso.CreateTextFile(ZipFilePath,true) File.Write "PK"& Chr(5) & Chr(6) & String(18, Chr(0)) File.Close Set objShell=CreateObject("Shell.Application") Set objSource=objShell.NameSpace(SourceFolder) Set objFolderItem=objSource.Items() Set objTarget=objShell.NameSpace(ZipFilePath) intOptions=256 objTarget.CopyHere objFolderItem,IntOptions Do wait 0.5 Loop Until objTarget.Items.Count >0 Set File=Nothing Set objShell=Nothing Set WScript=Nothing End Function 'FunctionName:SendMail 'Description:SendMail to specify user and specify server 'InputParameter:useraccount,userpassword,serveraddress,sendto,mailsubject,mailbody,attachment 'FunctionType:Framework Core function Function SendMail(UserAccount,UserPassword,SendTo,MailSubject,MailBody,Attachment) NameSpace="http://schemas.microsoft.com/cdo/configuration/" Set Email=CreateObject("CDO.Message") Email.From=UserAccount Email.To=SendTo Email.Subject=MailSubject Email.TextBody=MailBody If Attachment<>"" Then If FileExists(Attachment) Then Email.AddAttachment Attachment End If End If AccountInfo=split(UserAccount,"@",2,1) With Email.Configuration.Fields .item(NameSpace&"sendusing")=2 .item(NameSpace&"smtpserver")="smtp."+AccountInfo(1) .item(NameSpace&"smtpserverport")=25 .item(NameSpace&"smtpauthenticate")=1 .item(NameSpace&"sendusername")=AccountInfo(0) .item(NameSpace&"sendpassword")=userpassword .Update End With Email.Send Set Email=nothing End Function
再补充几个功能,进入节点,和退出QTP自带report结点
'FunctionName:EnterNode 'Description:insert a node,then all event will be logged under this node 'InputParameter:NodeName---TestInteration or ActionInteration;NodeContent,Node description 'OutPutParameter:Node 'FunctionType:Reporting module,Referenced by LogAndCaptureImage '@Description Create a Node ,then all reporter event will under this node,this function must work together with ExitNode Public Function EnterNode(ByRef NodeName, ByRef NodeContent) Set DiscriptionNodeData=CreateObject("Scripting.Dictionary") DiscriptionNodeData("Status")=MicDone DiscriptionNodeData("PlainTextNodeName")=NodeName DiscriptionNodeData("StepHtmlInfo")=NodeContent DiscriptionNodeData("DllIconIndex")=210 DiscriptionNodeData("DllIconSelIndex")=210 DiscriptionNodeData("DllPath")=Environment("ProductDir")+"/bin/ContextManager.dll" intContext=Reporter.LogEvent("User",DiscriptionNodeData,Reporter.GetContext) Reporter.SetContext intContext End Function 'FunctionName:ExitNode 'Description:Exit current node 'InputParameter:None 'OutputParameter:Node 'FunctionType:Reporting Module,referenced by LogAndCaptureImage '@Description Exit current node Public Function ExitNode() Reporter.UnSetContext Set DiscriptionNodeData=Nothing End Function