前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >python处理testlink

python处理testlink

作者头像
赵云龙龙
发布2019-09-03 18:39:54
2K0
发布2019-09-03 18:39:54
举报
文章被收录于专栏:python爱好部落python爱好部落

在软件活动中,我们需要对测试用例进行管理,如果只用excel,不用管理工具系统的管理,那么将出现以下一些问题: 案例文件分散,测试进度不透明; 需求变更导致的测试计划/测试用例变更,未能及时通知相关测试人员; 版本管理困难,很难追踪版本的变化; 缺陷管理与测试用例管理脱节,不便于缺陷密度的分析; 产品需求、测试计划、测试用例未能建立关联,不便于测试过程管理; 缺乏相关的测试分析报告数据,不便于暴露测试风险;

市面上的测试管理工具比较多,常见的有jira, Bugzilla, 禅道,TestLink等。

如果有经费,可以用jira, 这个可以完美的跟confluncen里的文档和jira里面的bug链接起来。

免费的可以用testlink.

但是免费的东西,还是有缺陷的:

case不能批量执行,一个个都要手动去标记,关键是点一次要刷新整个页面,等待时间特长。

系统经常打不开,或者响应时间过长,效率特低。

写case不是很方便,更新和维护也不方便。

用例导入只支持xml格式。

既然这么多问题,咋不更新或者用其它系统?我也想换哈,可是很多事情不是我们说了算。 既然不能换,那么我们就想办法曲线救国吧。

下文就是用python来解析xml, 用它来生成excel来执行,并将结果批量更新。这样就简单脱离了这个系统,从而提高效率。

我们先从case里面导出xml. 大概是这样的,我们要获得此次要执行的全部case的id.

应该使用哪个 XML 库?

Python 有非常多的工具来处理 XML。我们常常不知道用哪个更好。

xml.dom.* 模块 - 是 W3C DOM API 的实现。如果你有处理 DOM API 的需要,那么这个模块适合你。

xml.sax.* 模块 - 是 SAX API 的实现。这个模块牺牲了便捷性来换取速度和内存占用。SAX 是一个基于事件的 API,这就意味着它可以“在空中”(on the fly)处理庞大数量的的文档,不用完全加载进内存。

xml.parser.expat - 是一个直接的,低级一点的基于 C 的 expat 的语法分析器(见注释2)。 expat 接口基于事件反馈,有点像 SAX 但又不太像,因为它的接口并不是完全规范于 expat 库的。

最后,我们来看看 xml.etree.ElementTree (以下简称 ET)。它提供了轻量级的 Python 式的 API ,它由一个 C 实现来提供。相对于 DOM 来说,ET 快了很多,有很多令人愉悦的 API 可以使用。相对于 SAX 来说,ET 也有 ET.iterparse 提供了 “在空中” 的处理方式,没有必要加载整个文档到内存。

我的建议 是尽可能的使用 ET 来处理 XML ,学好ET就可以了,其它的可以不用学。

读入

首先读入XML,有两种途径,从文件读入和从字符串读入。 从文件读入:

代码语言:javascript
复制
import xml.etree.ElementTree as ET
tree = ET.parse('sample.xml')
root = tree.getroot()

从字符串读入:

代码语言:javascript
复制
root = ET.fromstring(sample_as_string)
获得属性

查看一个Element的Tag和Attribute的方法,Tag是一个字符串,而Attribute得到的是一个字典。

另外,还可以使用

代码语言:javascript
复制
Element.get(AttributeName)

来代替Element.attrib[AttributeName]来访问。

查看节点

查看孩子节点: root.attrib返回的是一个空字典,如果看root的孩子,可以得到非空的attrib字典。

使用for...in...访问
代码语言:javascript
复制
for child in root:
    print child.tag, child.attrib
下标访问:
代码语言:javascript
复制
print root[0].tag
print root[0][0].tag
使用tag访问:

下标访问的方法虽然简单,但是在未知XML具体结构的时候并不适用,通过Tag名称访问的方法更具有普适性。这里用到Element类的几个函数,分别是

代码语言:javascript
复制
Element.iter()
Element.findall()
Element.find()

这两个函数使用的场景有所差异: Element.iter()用来寻找所有符合要求的Tag,注意,这里查找的范围是所有孩子和孩子的孩子 and so on。如果查看所有的某个tag,可以使用下面的代码:

代码语言:javascript
复制
for neighbor in root.iter('tag'):
    print neighbor.text

Element.findall()只查找直接的孩子,返回所有符合要求的Tag的Element,而Element.find()只返回符合要求的第一个Element。

查看Element的值

我们可以直接用Element.text来得到这个Element的值。

xpath:

xml.etree.ElementTree可以通过支持的有限的XPath表达式来定位元素。 ElementTree支持的语法如下:

tag 查找所有具有指定名称tag的子元素。例如:country表示所有名为country的元素,country/rank表示所有名为country的元素下名为rank的元素。

代码语言:javascript
复制
*   查找所有元素。如:*/rank表示所有名为rank的孙子元素。
.   选择当前元素。在xpath表达式开头使用,表示相对路径。
//  选择当前元素下所有级别的所有子元素。xpath不能以“//”开头。
..  选择父元素。如果视图达到起始元素的祖先,则返回None(或空列表)。起始元素为调用find(或findall)的元素。
[@attrib]   选择具有指定属性attrib的所有子元素。
[@attrib='value']   选择指定属性attrib具有指定值value的元素,该值不能包含引号。
[tag]   选择所有具有名为tag的子元素的元素。
[.='text']  Python3.7+,选择元素(或其子元素)完整文本内容为指定的值text的元素。
[tag='text']    选择元素(或其子元素)名为tag,完整文本内容为指定的值text的元素。
[position]  选择位于给定位置的所有元素,position可以是以1为起始的整数、表达式last()或相对于最后一个位置的位置(如:last()-1)
方括号表达式前面必须有标签名、星号或者其他方括号表达式。position前必须有一个标签名。

OK,我们来看官方的例子

代码语言:javascript
复制
<?xml version="1.0"?>
<data>
    <country name="Liechtenstein">
        <rank>1</rank>
        <year>2008</year>
        <gdppc>141100</gdppc>
        <neighbor name="Austria" direction="E"/>
        <neighbor name="Switzerland" direction="W"/>
    </country>
    <country name="Singapore">
        <rank>4</rank>
        <year>2011</year>
        <gdppc>59900</gdppc>
        <neighbor name="Malaysia" direction="N"/>
    </country>
    <country name="Panama">
        <rank>68</rank>
        <year>2011</year>
        <gdppc>13600</gdppc>
        <neighbor name="Costa Rica" direction="W"/>
        <neighbor name="Colombia" direction="E"/>
    </country>
</data>

XPath表达式用来在XML中定位Element,下面给一个例子来说明:

代码语言:javascript
复制
import xml.etree.ElementTree as ET

root = ET.fromstring(countrydata)

# Top-level elements
root.findall(".")

# All 'neighbor' grand-children of 'country' children of the top-level
# elements
root.findall("./country/neighbor")

# Nodes with name='Singapore' that have a 'year' child
root.findall(".//year/..[@name='Singapore']")

# 'year' nodes that are children of nodes with name='Singapore'
root.findall(".//*[@name='Singapore']/year")

# All 'neighbor' nodes that are the second child of their parent
root.findall(".//neighbor[2]")

好的,再来实践一下我们testlink导出的xml文件:

代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?>
<!-- TestLink - www.testlink.org - xml to allow results import -->
<results>
    <testproject name="Labs" prefix="ET" />
    <testplan name="OAp" />
    <build name="2.0" />
    <platform name="Android" />

    <testcase external_id="ET-81835">
        <result>X</result>
        <notes>test link rocks </notes>
        <tester>put login here</tester>
        <!-- if not present now() will be used -->
        <timestamp>YYYY-MM-DD HH:MM:SS</timestamp>
        <bug_id>put your bug id here</bug_id>
        
        
    </testcase>

    <testcase external_id="ET-81836">
        <result>X</result>
        <notes>test link rocks </notes>
        <tester>put login here</tester>
        <!-- if not present now() will be used -->
        <timestamp>YYYY-MM-DD HH:MM:SS</timestamp>
        <bug_id>put your bug id here</bug_id>
        <steps>
<step>
    <step_number>1</step_number>
    <result>p</result>
    <notes>your step exec notes</notes>
</step>
</steps>
        
    </testcase>

代码敲起来

代码语言:javascript
复制
import xml.etree.ElementTree as ET
tree = ET.parse('/Users/anderson/Documents/OneApp_Android.xml')
root = tree.getroot() #获得root节点, 根节点应该是result
print(root)

print(root[5].attrib['external_id']) #下标访问第六个子节点,是testcase, 通过attrib可以获取caseid
# print(root[5][0].text) #下标访问第六个子节点下的第一个孙子节点,, 通过attrib可以获取text,应该是X
# print(root[6].attrib['external_id'])
# print(root[6][0].text)

for t in root.iter():
    print(t.tag,t.attrib)

# for t in root:
#     print(t.attrib)
#     print(t.tag)

# for t in root:
#     print(t.get("external_id"))


# for t in root.iter("testcase"):
#     print(t.attrib)

# for t in root.iter("testcase"):
#     print(t.attrib['external_id'])

# for t in root.findall("testcase"):
#     print(t.attrib['external_id'])

# for t in root.find("testcase"):
#     print(t.attrib)

# for elem in tree.iterfind('testcase'):
#     print(elem.attrib)

# for elem in tree.iterfind('testcase/result'):
#     print(elem.text)

count = 0
for elem in tree.iter(tag='testcase'):
    if elem[0].text == 'X':
        count += 1
print(count)

敲完这些,基本就掌握了。是不是很简单?

修改XML

前面已经介绍了如何获取一个Element的对象,以及查看它的Tag、Attribute、值和它的孩子。下面介绍如何修改一个Element并对XML文件进行保存

修改Element

修改Element可以直接访问Element.text。 修改Element的Attribute,也可以用来新增Attribute:

代码语言:javascript
复制
Element.set('AttributeName','AttributeValue')

新增孩子节点:

代码语言:javascript
复制
Element.append(childElement)

删除孩子节点:

代码语言:javascript
复制
Element.remove(childElement)

保存XML 我们从文件解析的时候,我们用了一个ElementTree的对象tree,在完成修改之后,还用tree来保存XML文件。

代码语言:javascript
复制
tree.write('output.xml')
构建XML

ElementTree提供了两个静态函数(直接用类名访问,这里我们用的是ET)可以很方便的构建一个XML,如:

代码语言:javascript
复制
root = ET.Element('data')
    country = ET.SubElement(root,'country', {'name':'Liechtenstein'})
    rank = ET.SubElement(country,'rank')
    rank.text = '1'
    year = ET.SubElement(country,'year')
    year.text = '2008'
    ET.dump(root)

就可以得到

代码语言:javascript
复制
<data><country name="Liechtenstein"><rank>1</rank><year>2008</year></country></data>

掌握了这些知识后,我们有两种方法来操作了,第一种方法是手工操作,导出导入xml. 第二种方法是调用testlink API。 我现在的方法是结合这两种, 先将xml导出来,获取到caseid, 然后调用API,获取到case的具体信息,导出到EXCEL, 然后在EXCEL中执行,最后将excel里面的信息,更新到xml中,导入到testlink。 先需要安装API

代码语言:javascript
复制
pip install TestLink-API-Python-client

将导出的xml的case id搜集起来:

代码语言:javascript
复制
import xml.etree.ElementTree as ET

import numpy as np
import pandas as pd

case_list = []

tree = ET.parse("/Users/Anderson/Downloads/OneApp_Android.xml")
root = tree.getroot()

for t in root.findall("testcase"):
    # print(t.attrib["external_id"])
    case_list.append(t.attrib["external_id"])

有了这个列表,得到case信息,得到excel, 先要获得一个key, 如图:

代码语言:javascript
复制
import testlink

def get_case_detail(id):

    # 连接test link
    url = "host/testlink/lib/api/xmlrpc/v1/xmlrpc.php"
    key = "xxx"
    tlc = testlink.TestlinkAPIClient(url, key)
    test_case = tlc.getTestCase(None, testcaseexternalid=id)

    return id, test_case[0]["name"],test_case[0]["summary"],test_case[0]["steps"]

将url的host换成自己的,key替换掉,就可以拿到case的信息了。 api的具体参数请看官网。 然后我们就可以得到一个excel了,利用pandas,简单方便,比直接操作excel简单很多。

代码语言:javascript
复制
    get_case = (get_case_detail(x) for x in case_list)
    case_pd = pd.DataFrame(get_case, columns=["id","name","summary","steps"])
    case_pd["result"] =""

    with pd.ExcelWriter('output.xlsx') as writer:
        case_pd.to_excel(writer, sheet_name='Sheet_name_1')

然后就可以在Excel里面执行。 执行完成后,就可以将excel里的结果,更新到xml文件中。

代码语言:javascript
复制
def put_result():
    df = pd.read_excel('/Users/anderson/Downloads/output.xlsx')
    data = df[["id","result"]]
    print(data)

    for ix, row in data.iterrows() : t in root.iterfind("testcase/external_id"):
        if row['id'])== t.attrib:
            t.set('result', row['result'])
            t.set('tester', 'xxx')
            t.set('timestamp', time.now())

     tree.save("result.xml")

好了,我们可以手动将xml文件导入到testlink,就完成了我们这次实践之旅。

在case比较多的情况下,这种曲线救国的策略,效率会提升很多。

更多精彩,请关注微信公众号:python爱好部落

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-08-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 python爱好部落 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 应该使用哪个 XML 库?
    • 读入
      • 获得属性
        • 查看节点
          • 使用for...in...访问
            • 下标访问:
              • 使用tag访问:
                • 查看Element的值
                  • xpath:
                    • 修改XML
                      • 修改Element
                        • 构建XML
                        相关产品与服务
                        测试管理
                        CODING 测试管理(CODING Test Management,CODING-TM)为您提供井然有序的测试协同管理工具服务,从测试用例库管理、制定测试计划,到协作完成测试任务,为测试团队提供敏捷测试工作方式,提高测试与研发团队的协同效率。提供可视化的工作视图以及数据报告,随时把控测试进度和规划。
                        领券
                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档