摘要:
本文介绍了如何使用 Jenkins 和 TestNG 实现满足复杂测试需求的”自助式”自动化测试平台。该方案以 Jenkins 作为平台的基础,结合功能强大的插件及系统配置,部署基于 TestNG 的自动化测试包,并提供了友好的 Web 访问界面。项目成员可以在任何时间和地点,通过浏览器访问该平台,而且可以按照不同需求选择测试环境、测试集、测试用例,并提交自动化测试请求,达到真正的“自助式”自动化测试。该平台它可以极大地提高开发和测试团队自动化脚本的使用效率和便捷性。
目录:
正文:
一. 提出需求
测试部开发一套自己的质量中心,主要用于缺陷统计、接口自动化测试、APP自动化测试、在线监控等,在接口自动化测试和APP自动化测试过程中,我们需要实现,用户选择不同的测试集合,集合中包含哪些测试用例,TestNG会自己去执行不同的测试用例,做到根据用户不同的输入做出不同的响应。
二. 方案设计
三. 编码
app_elements:存储app页面控件,如Android的resource id,iOS的xpath
app_execute: 存储支持执行结果,测试报告、测试结果等
app_mobile: 存储测试机型的相关信息
app_modules: 存储测试APP中包含的模块,分层的思想,方便管理
app_platform: 存储测试APP,支持多个APP
app_suitecase: 存储测试集合与测试用例的关系,一个测试用例对应多个测试集合
app_testcase:存储测试用例
app_testjob:存储测试任务,关联相应的Jenkins路径,直接多地执行
app_testsuite:存储测试集合
TestPlatform: 测试平台,是Android还是iOS
TestDevice: 测试设备,Android需要传入udid,iOS不需要
TestEnv: 测试环境,qa还是live
TestJobId: 测试任务编号,通过这个任务编号可以MySQL查询到关联的测试集合以及测试集合中的测试用例
TestExecuteId: 执行任务编号,传入Python脚本,讲生成的测试报告emailable-report.html存放相应的位置
3.3 Python文件生成TestNG XML
# -*- coding:utf-8 -*-
import os
import MySQLdb
import sys
import xml.dom.minidom
# 外部传入的测试任务编号
test_job_id = sys.argv[1]
# 连接db
def connect_db(db):
db = MySQLdb.connect(host="10.9.8.20",
port=3306,
user="***",
passwd="***",
db=db,
charset="utf8")
return db
# 请求mysql获取数据
def get_data(db, sql):
conn = connect_db(db)
cur = conn.cursor()
cur.execute(sql)
data = cur.fetchall()
cur.close()
conn.commit()
conn.close()
return data
# 判断文件是否存在,如果不存在立即创建,如果存在,则立即删除,重新覆盖
def xml_exist():
file_name = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + "/resources/YouYu_Stock.xml"
if os.path.exists(file_name):
os.remove(file_name)
f = open(file_name, "w")
f.close()
return file_name
# 获取测试用例英文名
def get_case_name():
suite_id = get_data("app", "select suite_id from app_testjob where id=" + test_job_id)[0][0]
case_id = get_data("app", "select case_id from app_suitecase where suite_id=" + str(suite_id))
list_case_name = []
for i in xrange(0, len(case_id)):
case_name = get_data("app", "select ename from app_testcase where id=" + str(case_id[i][0]))[0][0]
list_case_name.append(case_name)
return list_case_name
def main():
file_name = xml_exist()
case_names = get_case_name()
doc = xml.dom.minidom.Document()
root = doc.createElement("suite")
root.setAttribute("name", "TestSuite")
root.setAttribute("parallel", "false")
doc.appendChild(root)
# 添加parameter
nodeManager = doc.createElement("parameter")
nodeManager.setAttribute("name", "url")
nodeManager.setAttribute("value", "127.0.0.1")
root.appendChild(nodeManager)
nodeManager = doc.createElement("parameter")
nodeManager.setAttribute("name", "port")
nodeManager.setAttribute("value", "4727")
root.appendChild(nodeManager)
nodeManager = doc.createElement("parameter")
nodeManager.setAttribute("name", "device")
nodeManager.setAttribute("value", "${TestPlatform}")
root.appendChild(nodeManager)
nodeManager = doc.createElement("parameter")
nodeManager.setAttribute("name", "udid")
nodeManager.setAttribute("value", "${TestDevice}")
root.appendChild(nodeManager)
nodeManager = doc.createElement("parameter")
nodeManager.setAttribute("name", "env")
nodeManager.setAttribute("value", "${TestEnv}")
root.appendChild(nodeManager)
# 添加test case
for i in xrange(0, len(case_names)):
print case_names[i]
node_test = doc.createElement("test")
node_test.setAttribute("name", case_names[i])
node_classes = doc.createElement("classes")
node_test.appendChild(node_classes)
node_class = doc.createElement("class")
node_class.setAttribute("name", "com.youyu.stock.automation.mobile.testcase.registerAndLogin.RegisterAndLoginTestCase")
node_classes.appendChild(node_class)
node_methods = doc.createElement("methods")
node_class.appendChild(node_methods)
node_include = doc.createElement("include")
node_include.setAttribute("name", case_names[i])
node_methods.appendChild(node_include)
root.appendChild(node_test)
f = file(file_name, "w")
doc.writexml(f, "\t", "\t", "\n", "utf-8")
f.close()
if __name__ == '__main__':
main()
Maven pom.xml定义:
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-surefire-pluginartifactId>
<version>2.19.1version>
<configuration>
<systemPropertyVariables>
<testEnvironment>${TestDevice}testEnvironment>
<testEnvironment>${TestEnv}testEnvironment>
<testEnvironment>${TestJobId}testEnvironment>
systemPropertyVariables>
<suiteXmlFiles>
<suiteXmlFile>${automationFile}suiteXmlFile>
suiteXmlFiles>
configuration>
plugin>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<configuration>
<source>1.8source>
<target>1.8target>
configuration>
plugin>
plugins>
pluginManagement>
build>
3.5 生成的TestNG XML文件,例如:
3.6 生成的emailable-report.html存储在MySQL中
# -*- coding:utf-8 -*-
import os
import sys
import MySQLdb
from bs4 import BeautifulSoup
# 外部传入执行任务时参数的编号
execute_id = sys.argv[1]
# 连接db
def connect_db(db):
db = MySQLdb.connect(host="10.9.8.20",
port=3306,
user="***",
passwd="***",
db=db,
charset="utf8")
return db
# 请求mysql获取数据
def get_data(db, sql):
conn = connect_db(db)
cur = conn.cursor()
cur.execute(sql)
data = cur.fetchall()
cur.close()
conn.commit()
conn.close()
return data
def write_result():
file_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + "/target/surefire-reports/emailable-report.html"
f = open(file_path, "r")
html = f.read()
# 测试结果写入MySQL
soup = BeautifulSoup(html)
PassCase = int(soup.find_all("th", class_="num")[0].get_text())
FailCase = int(soup.find_all("th", class_="num")[2].get_text())
# 测试报告写入MySQL
html = MySQLdb.escape_string(html)
get_data("app", "update app_execute set test_result=\"%s\", test_report=\"%s\" where id=%s" % (str(PassCase) + "/" + str(PassCase+FailCase), html, str(execute_id)))
if __name__ == '__main__':
write_result()
四. 测试
总结:
为了实习根据用户不同的输入做出不同的相应,期间尝试方案如下:
参考文献:
集成 Jenkins 和 TestNG 实现自助式自动化测试平台
http://testng.org/doc/index.html