转自 http://www.ibm.com/developerworks/cn/rational/r-cn-lsvbaapitesting/
应用程序往往提供了可扩展的脚本环境,如在 Lotus Notes 和 Microsoft Office 等中所提供的 LotusScript、Visual Basic for Applications(VBA) 等,用户可基于这些脚本环境对应用进行二次开发和功能扩展。对脚本语言 VBA 的支持是 IBM Lotus Symphony 的一个新的重要功能,用户可以基于 IBM Lotus Symphony 开发自己的应用程序。
然而,应用程序能进行扩展的基础是脚本环境提供大量的脚本语言 API,而如何对这种脚本语言的 API 进行自动化测试是一个值得探讨的问题。通常进行自动化测试一般需要借助测试工具,比如对有图形界面的产品进行自动化测试往往采用 Rational Functional Tester(RFT)等自动化测试工具。如果直接使用 RFT 对应用程序脚本语言 API 进行自动化测试,需要维护大量的 RFT 脚本,因此本文将介绍一种高效的应用程序脚本语言进行 API 自动测试的新方法。
本文首先对 API 测试进行了分析,然后给出了自动测试脚本语言 API 的思路和方法,接着对本文中介绍的方法进行了详细阐述和分析,最后本文还将介绍结合使用 RFT 对应用程序脚本语言进行 API 自动测试的方法,从而进一步提高了测试效率。上文提到 IBM Lotus Symphony 对 VBA 的支持是 IBM Lotus Symphony 一个重要功能,包含了上千个 API 需要支持,使用本文介绍的测试方法,极大的节省了开发和维护脚本的时间,提高了测试效率。
对 API 测试是一个常见的话题。根据 API 的执行效果,可以把 API 分为两类(1)有界面 User Interface(UI)效果的 API(2)无界面效果的 API。因此对 API 进行测试,从实现的角度来看,有两种方法(1)验证图形界面的效果(2)验证返回值。如图 1 所示。
脚本语言有和高级语言(如 Java,C++ 等)相比有其自身的特点,脚本语言如 Visual Basic for Applications(VBA), Star Basic, LotusScript. 等之类的语言一般和某个应用程序结合在一起使用。 VBA 在 Mircosoft Office 中使用,一个 Excel 文件中包含 VBA 代码,如图 2 所示。
LotusScript. 在 Lotus Notes 等 Lotus 产品中使用,如 NSF 文件中包含 LotusScript. 代码等。脚本语言同高级语言相比,必须依附于应用文件而存在,用户可以比较方便的基于某个产品进行扩展和定制开发自己的所喜欢的应用程序。然而如何对脚本语言形式的 API 进行自动化测试探讨较少。通常,对有界面操作的功能测试进行自动化一般使用 Rational Functional Tester(RFT)等测试工具;对高级语言(如 Java)形式的 API 等使用 JUnit 等进行测试;对脚本语言的 API,很难做到像高级语言那样方便的操作和控制。
如果按照 RFT 测试普通 GUI 的思路对含有脚本语言形式的 API 进行自动化测试,通常需要对包含这些 API 的测试文件进行验证,即把测试文件作为需要测试的应用程序对待。也就说是使用 RFT 对脚本语言形式的 API 进行测试需要:(1)创建使用测试对象—API 的测试文件(2)使用 RFT 编写测试脚本对测试文件进行验证。这样对一个脚本语言形式的 API 进行自动化测试需要维护(1)包含测试对象的测试文件(2)需要维护 RFT 测试脚本,一旦被测的 API 有变动,需要很大的精力进行维护。
按照问题分析一节中图 1 的分类方法,API 可以分为 2 类,对于第 1 类 API,需要验证图形界面效果的,如果进行自动化测试则需要使用 RFT 等测试工具对 GUI 进行验证。而第 2 类 API,只需要验证返回值,不需要验证界面效果,不必使用 RFT 等测试工具进行辅助。因此本文借鉴 RFT 和 Junit 进行自动化测试的思想,使用 RFT 对对第 2 类不需要验证界面效果的 API 进行自动测试。从实现的角度来看测试工程,一般包括:
(1)测试工程的组织结构
对于测试工程的组织,使用 RFT 对 GUI 进行自动化测试有成熟的解决方案,工程组织一般采用分层的结构, AppObjects 层(驱动应用程序的 Objects),Task 层(完成常见操作),测试脚本层(测试用例脚本)。使用 Junit 等进行自动化测试时,也往往会对测试工程进行设计或分层的结构,不同的层负责完成不同的功能,从而达到易于维护的目的。
(2)测试文件的组织
测试文件,也就是测试脚本,是对测试用例的实现。为了方便的组织和使用测试脚本, Junit 中的每个测试方法都使用 test 为前缀来命名, RFT 中使用 testMain 作为执行测试脚本的入口。
(3)验证点的比较
自动化测试的一个核心就是对验证点的处理,只有能自动处理验证点,才能进行自动化测试,无论普通的功能测试还是对 API 进行测试都不例外。功能测试验证点的处理往往是通过其控件状态,对话框标题等进行验证;对 API 进行测试,可以通过其返回值,或者借助其它 API 返回结果来进行验证。在 JUnit 中对验证点使用 assertEquals, assertNoNull 等方法对验证点进行处理,自动返回测试结果。
(4)测试结果生成
通常根据验证点的结果,生成测试结果。
(5)测试的执行
对测试脚本进行连续自动执行。
因此,对应用程序脚本语言的 API 进行自动化测试,也可以采用上文介绍的方法进行组织和执行。下文将一一进行详细阐述。
VBA 之类的脚本语言不能像 Java、C++ 等高级语言单独存在,它们往往和其应用文件一起存在,如一个 Excel 文件可能包含 VBA 代码(ALT+F11 可以看到其代码)。因此对此类脚本语言的 API 进行测试,首先需要创建相应的应用程序文件来使用这些 API,如创建一个 Excel 文件。如对 VBA 中的 API : Worksheets.Count 进行测试,需要在 test.xls 中使用方法 Worksheets.Count,因此测试要测试 API :Worksheets.Count 就必须创建一个 Excel 文件。如图 3 所示。
虽然脚本语言不能像高级语言那样方便的在一个工程中通过分层等对代码工程进行优化,但我们仍然可以借鉴上文第 2 节测试工程组织结构提到的 RFT 组织测试文件的思想,尽可能的对代码复用。而从功能的视角来看,测试工程或者测试工具一般需要具有以下功能(1)编写测试脚本,用于实现测试用例(2)对验证点进行处理的功能,用于实现对验证点的比较,判断验证点 Fail 或者 Pass(3)执行过程中的 Log 的处理功能,用于对执行过程中结果写文件日志等(4)错误处理功能,用于执行过程中发生的错误进行恢复和处理。
因此对上文中提到每一个功能使用一个模块(Module)来完成。除了每个测试用例必须要和测试文件一起之外,其它的部分可以和测试用例分离,做成加载宏来供测试用例调用。
对于每个测试脚本来说,本文结合 JUnit 和 RFT 组织测试脚本的方法,(1)对每个测试方法使用 test 为前缀名,方便组织和阅读(2)使用 testMain 作为执行入口,调用测试方法。如对方法 Worksheets.Count、Worksheet.Name 进行测试,我们按下列结构进行组织。
清单 1. 对方法 Worksheets.Count、Worksheet.Name 进行测试
Sub testMain() testWorksheetsCount testWorksheetName End Sub Sub testWorksheetsCount() …. //Test Script. End Sub Sub testWorksheetName() ….//Test script. End Sub |
不存在 VBA 语言的类似 JUnit 的测试 API 的工具。根据上文第 2 节中阐述的思想,可以自己编写相应的验证点比较方法。只需要函数返回的真实值和期望值进行比较就可以得到验证点的测试结果,即和真实值和期望值相等,这验证点通过,反之则验证点失败。为了使结果更有可读性,我们可以提供验证点的描述信息。例如验证方法:assertEquals,需要传入期望值,真实值,描述信息,同时把比较结果直接写入文本文件。测试用例脚本中,在验证点处可以调用此方法对验证点进行判断,自动生成测试结果。脚本如下所示:
清单 2. 在验证点处可以调用此方法对验证点进行判断,自动生成测试结果
Sub assertEquals(realValue As Variant, expectValue As Variant, testDescription As String) Dim vpResult As Boolean vpResult = TestAreEqual(realValue, expectValue) Dim testMsg As String testMsg = "---------------------VP Start---------------------" + Chr(13) + Chr(10) If vpResult = True Then testMsg = testMsg + "VP:Result=Pass" + Chr(13) + Chr(10) Else testMsg = testMsg + "VP:Result=Fail" + Chr(13) + Chr(10) Test_Result = False End If testMsg = testMsg + "VP:Desciption=" + testDescription + Chr(13) + Chr(10) testMsg = testMsg + "VP:RealValue=" + CStr(realValue) + Chr(13) + Chr(10) testMsg = testMsg + "VP:ExpectValue=" + CStr(expectValue) + Chr(13) + Chr(10) testMsg = testMsg + "----------------VP End-----------------------" + Chr(13) + Chr(10) If TESTLOG_VERBOSE Then TestLog_ITEM testMsg End If End Sub |
测试的过程中往往会有异常发生或验证点失败,为了测试执行结束后对异常或者测试失败情况进行分析,需要把测试执行过程的信息进行记录,因此需要有 Log 处理机制。我们把测试执行中步骤信息,测试说明,测试结果等写入文本文件。为了有可读性,同时方便分析 Log 文件生成更易读的报告文件,需要对 Log 文件的内容和格式进行定义。
生成的 Log 文件包括以下部分:(1)开始执行时间(2)验证点信息,包括验证点描述信息、验证点期望值、真实值、验证点的结果 (3) 执行结束时间(4)整个测试用例的执行结果。每个测试用例生成一个 Log 文件,使用测试文件名来对 Log 文件来命名。我们使用以下格式:
--------------------------VP Start-------------------------- VP:Result=Pass or Fail VP:Desciption=The verification point description information. VP:RealValue=The real value of test object VP:ExpectValue=The expect value of test object. --------------------------VP End---------------------------- |
测试执行过程中必然会遇到错误发生,为了使测试能够连续的执行,不会因为一个错误而中断了整个测试的执行过程,同时方便测试执行结束后分析错误原因,需要对运行时的错误进行处理。从测试的观点来看,测试用例是用来测试被测对象,测试脚本只需要严格按照测试用例中的步骤来编写。如果对测试脚本加了容错方法,则成为测试“测试脚本”,因此不应在每个测试脚本中加容错处理。我们只需要在 testMain 方法中加上容错处理,来捕获运行时的错误 , 运行时的错误描述信息写入 Log 文件。代码片段如下:
Sub testMain() On Error GoTo ErrorProcess testMothed1() testMothed2() .... Exit Sub ErrorProcess: TestLog_Error (Err.Description) Resume.Next end sub |
上文中我们提到一个或多个 API 存在一个测试文件中,也就是一个测试脚本存在于一个测试文件中。它不像 JUnit 等所有测试脚本都在一个工程中,而且有 TestSuite 机制可以方便的连续执行需要的测试脚本。RFT 运行测试脚本时,会自动运行 testMain 方法,且脚本都在一个 Java 工程中,可以方便的连续执行所有的测试脚本。上文中介绍到本文测试脚本的组织结构,每个测试脚本都存在 testMain 方法中,因此我们只需要运行测试文件,调用 testMain 方法既可脚本以运行测试脚本。我们借鉴这种运行测试脚本的机制,只要可以连续处理测试文件,测试脚本就可以被连续的执行。我们使用 VBA 语言来操作测试文件,连续执行测试脚本。也可以使用其它高级语言来操作测试文件,启动测试脚本。考虑到 Symphony 项目中对 VBA 只是部分支持,我们使用 Java 去操作测试文件,执行测试脚本,Symphony Toolkit 中存在相应 Java API 可以读取和操作测试文件。图 4 所示其流程图:(1)读取测试文件列表(2)逐个打开测试文件(3)调用测试脚本。对于其它脚本语言也可以根据此思想通过其它方法进行操作。如使用 RFT 打开测试文件,模拟用户去调用测试脚本等。
在 Symphony 项目中我们使用基于 Domino 和 Lotus Notes 的 TestCase Database 对测试用例和测试执行进行管理。测试存在不同的测试阶段如 FVT,SVT,即使是一个测试阶段如 FVT 又可以分为 FVT Fist Pass,Regression Test 等,并且需要在不同的操作系统上进行测试,需要执行不同的测试用例集合。每个测试用例中包含了所需要的测试文件(Excel 文件),测试对象(被测 API 列表)等。一个测试用例可以在多个测试 Cycle 中执行,而每个测试执行文档记录都有一个唯一的 URL,因此我们可以根据测试需要,生成由 URL,测试文件名组成的 TestSuite 文件。从而根据 TestSuite 来执行需要被执行的测试脚本。我们使用 Java 代码去操作 TestCase Database 生成 TestSuite。其核心代码片段如下:
清单 4. 使用 Java 代码去操作 TestCase Database 生成 TestSuite
NotesThread.sinitThread(); // 初始化连接 Notes DB 线程 Session session = NotesFactory.createSession( (String)null, (String)null, password);// 获取连接 Notes DB 的 Seession Database database = session.getDatabase(host, nsf, false);// 获取 TestCase Database DocumentCollection dc = database.search("ExecCycleID=" + "\"" + cycle + "\""); // 根据 Cycle 获取测试记录集合 Document doc = dc.getFirstDocument(); // 把测试集合中的测试记录关键信息写入 TestSuite while (doc != null) { suiteFileWriter.write("-DexecutionURL=" + doc.getURL() + " " + tcID+ "\r\n"); doc = dc.getNextDocument(); } |
为了对测试执行过程进行管理,执行完测试脚本,需要把测试结果写入到 TestCase DB,达到对测试过程的追踪和记。一次测试过程往往需要对上千个 API 进测试,也就会有上千个执行记录,如果根据执行结果,手工去标记执行结果的状态会浪费大量时间,而且容易出错。从上文中可以看出,对每个测试脚本我们都会生成 Log 文件,Log 文件中包含测试脚本的执行结果和 Test CaseID,TestSuite 中含有可以唯一标识测试记录的 URL 和 TestCase ID,因此可以通过分析 Log 文件和 TestSuite 文件,把测试结果批处理写入到 Test Case Db 中。核心代码片段:
清单 5. 把测试结果批处理写入到 Test Case Db 中
NotesThread.sinitThread(); // 初始化连接 Notes DB 线程 Session s = NotesFactory.createSession((String)null, (String)null, password); Database database = s.getDatabase(host, nsf, false);// 获取 TestCase DataBase Document doc = db.getDocumentByUNID(getDocUNID(URL);// 获取测试记录文档 ... doc.replaceItemValue("ExecCountPassed", new Integer(doc.getItemValueInteger("ExecCountPassed") + 1));// 写入测试用例执行结果 doc.replaceItemValue("ExecDateLast", new Date().toString());// 写入执行时间 doc.save(); .... |
上文中介绍到执行每个测试脚本后都会生成一个 Log 文件。对整个测试来说,我们需要有一个概要的测试报告,而不是去查看所有 Log 结果文件。我们只需要分析 Log 文件和 TestSuite 文件,生成一个可读的测试包括,Symphony 项目中我们采用 XSL 定义生成一个 HTML 文件。对于 XSL 本文中不进行介绍。
本节将介绍结合 RFT 对脚步语言进行自动测试在 IBM Lotus Symphony 中的实践。上文第 8 节中提到了连续的执行的方法,因此我们只需使用 RFT 在 IBM Lotus Symphony 中连续打开包含需要测试的脚本 API 的文件,并点击一个 CommandButton,使脚本进行执行,其它的验证点处理等则由脚本语言自己处理。因此只需要编写一个通用的根据文件名,打开文件,并点击 CommandButton 的 RFT 脚本,不需要对每个测试文件编写一个 RFT 脚本。处理完所有的测试文件,则生成测试结果,并写入测试用例数据库 TCDB。如图 5 所示。
所用的 RFT 脚本核心代码如下:
public void testMain(Object[] args) { String fileName = ""; if (args != null && args.length != 1) return; else { fileName = (String) args[0]; } try { // 打开测试文件 CommonTask.openFile("sample/Macro/" + fileName); //Sheet1 上的 StarMacro 按钮,根据类型 ID 进行查找 VclButton btn = new VclButton(".find:58818 326 0"); // 触发调用脚本语言 API btn.click(); // 释放资源,关闭这个测试文件 CommonTask.discardAll(); } catch (Throwable t) { // 异常处理 } // 处理 VBA 脚本生成的 Log 文本文件,放到 Log 目录下 File logDir = new File(VBAConstant.OUT_DIR); if(!logDir.exists()) logDir.mkdirs(); File newLoc = new File(VBAConstant.OUT_DIR, fileName.substring(0, fileName.lastIndexOf(".xls")) + ".log"); boolean ret = VBAUtil.copyFile(logFile, newLoc); // 根据 Log 文件,分析脚本 API 执行结果 boolean result = false; String excResult = VbaLogProcessor.analyzeLog(logFile); if (excResult.equals(VbaLogProcessor.TEST_PASS)) { result = true; } else if (excResult.equals(VbaLogProcessor.TEST_FAIL)) { result = false; } else { Logger.error("Error Occured"); } Verify.verifyTrue(tcid + " result:", result); } |
根据上文的描述和以上代码,只需要使用 RFT 根据文件路径和名称在 IBM Lotus Symphony 中打开一个 Excel 测试文件,并点击 Sheet1 中的 CommandButton:StartMacro 就可以 VBA 脚本运行。如图 6 所示
StartMacro 触发的 VBA 脚本代码如下所示。
Sub Main() On Error GoTo ErrorProcess TestMain Exit Sub ErrorProcess: TestLogMacros.TestLog_Error (Err.Description) End Sub Sub TestMain() Dim testCaseID As String TestLogMacros.TestLog_ASSERTSetVerbose (True) TestLogMacros.TestLog_Start 'UserForm1.Show 0 testCount TestLogMacros.TestLog_End End Sub Sub testCount() TestLog_Verify ThisWorkbook.Worksheets.Count, 4, _ "Test WorkSheets.Count could work correctly" End Sub |
使用本文中介绍的方法,可以方便的对脚本语言 API 进行自动化测试。不仅节省了脚本维护的时间,也大大节省了对测试进行管理和执行的时间。使用本文的方法可以极大的提高对应用程序脚本语言 API 的测试效率。
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/14780873/viewspace-663184/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/14780873/viewspace-663184/