【摘要】进行白盒测试时,或者将测试用例和测试程序混在一起难以阅读;或者花很大精力构思用例的格式,然后编写较复杂的程序进行用例的提取;本文提出一种XML用例编写规范和解析思路,它基于python的XML解析器minidom,可以快速完成测试用例与测试程序分离。
鉴于XML在数据传输上的优越性,无论基于何种语言而开发的项目,采用基于XML进行用例编写,都将在通用性、兼容性和程序易编性上收获颇丰。
【关键词】白盒测试;XML;Python
Python的 XML解析器
终端UI采用了跨平台语言Python进行开发,保证了一套UI可以不用修改即可在众多主流操作系统上使用;采用python的解析器parse对XML进行解析的程序同样可以在这些操作系统上运行,这样我们的测试用例也是一套跨平台的用例,这种通用性极大的提高了效率。minidom在dom文件夹下,具体结构如下所示:
Python25/ Python2.5 安装根目录 (可执行文件的所在地)
|
+--lib/ 库目录 (标准库模块的所在地)
|
+-- xml/ xml包 (实际上目录中还有其它东西)
|
+--sax/ xml.sax包 (也只是一个目录)
|
+--dom/ xml.dom包 (包含 minidom.py)
|
+--parsers/ xml.parsers包 (内部使用)
Python解析一个XML包只需要两句话:
xmldoc = minidom.parse('pbkTestCase.xml')
第一句话导入我们的minidom模块,第二句话将指定的XML文件使用parse方法解析之,这是将整个XML文档一起读取解析的。从 minidom.parse 返回的对象是一个 Document 对象,它是 Node 类的一个子对象。这个Document 对象是连锁的 Python 对象的一个复杂树状结构的根层次,这些 Python 对象完整表示了传给 minidom.parse 的 XML 文档。每个 Node 都有一个 childNodes 属性,它是一个 Node 对象的列表,我们正是通过这种树型结构,逐层的找到所需要的数据。
XML测试用例格式约定
XML测试用例格式有如下三条约定:
(1) 以<function>和</function>作为一个测试函数的起始和结束标志;
(2) 以<ref>和</ref>作为一个测试用例的起始和结束标志;
(3) 以<p>和</p>作为一个测试用例的参数的起始和结束标志;
每个测试文本包含众多的 ”function” ;每个 ”function” 包含众多的 ”ref” ;每个 ”ref”包含众多的”p”。这样的XML文本便包含了众多的节点和子节点,采用上节提到的childNodes属性可以很容易的找到各个节点;3层结构使整个用例看起来比较明晰,而且每层使用统一的标识符,简化了解析程序的编写。一个完整的例子如下:
<?xml version="1.0"?>
<!DOCTYPE phonebook>
<phonebook>
<function id="test_CreatCategories">
<ref id="1">
<p> Mn*$</p>
<p>AppGlobals.TpvSimPbPC</p>
</ref>
<ref id="2">
<p>#12Ab&%</p>
<p>AppGlobals.TpvSimPbSIM</p>
</ref>
</function>
</phonebook>
上面是一个名片夹创建群组的两个用例,一个在PC侧建立名称为”Mn*$”的群组,另外一个是在SIM卡上创建名为”#12Ab&%”的群组。每个函数都有唯一的ID,便于解析程序提取此函数,ID的值不局限于数字,我们是将ID赋成每个测试函数的名字,这样解析程序将其作为字典的键,可以很明显的与测试函数对应,便于维护用例和程序。
测试用例的解析
上述测试用例的编写规则给解析用例带来了很大的便利。由于每个函数、每条用例及参数的标志固定,根据不同的ID即可将所需要的数据提取出来。解析过程如下:
(1) 通过getElementsByTagName属性找到每个测试函数;
(2) 使用attributes[u"id"].value,通过不同的ID,得到测试函数名,作为存储测试用例的字典的‘键’;
(3) 通过getElementsByTagName属性找到测试用例和用例参数,将其存入列表后,作为测试函数‘键’对应的‘值’存入字典;
(4) 添加一定的判断逻辑,给编写测试用例提供一些方便,比如测试用例的参数过长时,进行换行等。
下面是解析过程的python程序描述:
def importFile(path):
funcTC = {} #定义一个字典,用来存储测试用例的键-值
xmldoc = minidom.parse(path) #解析path路径下的某XML文件
funcList = xmldoc.getElementsByTagName_r(u'function')
for i in range(len(funcList)):
#根据不同的ID,得到每个测试函数的函数名
funcName = funcList[i].attributes[u"id"].value
#根据不同的ref和p,得到每个测试函数对应的测试用例其用例参数
refList = funcList[i].getElementsByTagName_r(u'ref')
refTC = [] # 测试用例存储到列表中
for j in range(len(refList)):
pList = refList[j].getElementsByTagName_r(u'p')
pTC = []# 测试用例的参数也存储到列表中
for k in range(len(pList)):
# 对用例参数的换行输入进行识别
temp1 = pList[k].firstChild.data.split(u'/n')
for w in range(len(temp1)):
# 对用例参数的空格和tab键进行识别
temp1[w]=temp1[w].strip(u'/t').strip()
temp2 = ''
for q in range(len(temp1)):
temp2 += temp1[q]
pTC.append(temp2)
refTC.append(pTC)
funcTC[funcName] = refTC # 测试用例存入字典中
return funcTC #返回我们从XML中提取的有效的测试用例数据
以我们上述创建群组的用例说明,我们的解析程序返回的数据如下:
funcTC={u'test_CreatCategories':[[u'Mn*$',u'AppGlobals.TpvSimPbPC'],[u'#12Ab&%', u'AppGlobals.TpvSimPbSIM']]}
测试用例在测试程序中的使用
由于有效的测试用例已经在字典中存储,测试程序可以非常方便的使用字典的键-值来获取需要的数据。测试程序只需要进行以下几步即可获取需要的数据:
from allTestCaseXml import importFile
pbkTestCase=importFile("./pbkTestCase.xml")
for i in range(len(pbkTestCase['test_CreatCategories'])):
[groupName,Flag]=pbkTestCase['test_CreatCategories'][i]
首先,导入importFile函数,获取存储有效用例的字典,最终从其中获取到需要的两个变量群组名groupName和存储位置Flag,测试程序即可使用这两个变量。
效果评价
本文提出的用例的分离思想简单易行,由于采用了XML编写用例,通用性很强,这样的一套用例不仅能够跨平台使用,也可以使用各种开发语言对其进行解析,适用于各种语言开发的终端软件。
测试用例与测试程序的分离使得用例编写和程序编写可以独立进行,编程人员可以把精力投入到测试程序的编写中,测试用例由其他人员编写和修改,既保证了用例的覆盖率,又提升了测试程序的开发效率。
参考资料
《Dive in Python》