视频地址:

http://www.chuanke.com/v1983382-135467-384869.html

这个内容其实已经在用了,我在上一篇文章robotium—只有apk文件的测试中已经讲过这个内容了,并且自己也用Python+wxpython写了界面程序,来实现跑case+获取xml运行结果+xml转html+发送邮件的功能

主要内容:

一、测试需求

1、统计每个case的执行时间

2、哪些case成功、失败

3、失败的case给出log

4、产生网页html报告结果

二、环境部署

以robotium为例,需要的环境:

1、JDK1.7、Android开发环境

2、Robotium的jar包

3、android-junit-report.jar包

三、报告生成原理

去官网瞅瞅:http://zutubi.com/,通过点击open source可进入下载页面下载

quick start

For the impatient, here is an overview of how to integrate the runner with Ant builds. Note all modifications are made to your test project, i.e. the project which implements the JUnit tests:

  • Grab the jar from the downloads page and add it to your libs/ directory.
  • Edit AndroidManifest.xml to set android:name in the <instrumentation> tag to:com.zutubi.android.junitreport.JUnitReportTestRunner.
  • Edit ant.properties to add the line:
    test.runner=com.zutubi.android.junitreport.JUnitReportTestRunner
  • Run your tests as you would normally:
    $ ant debug install test
  • Pull the resulting XML report from the device (from the application under test's internal storage directory):
    $ adb pull /data/data/<main app package>/files/junit-report.xml
  • Integrate the XML with your chosen build tool.

接下来就是原理:

1、com.zutubi.android.junitreport.JUnitReportTestRunner——具体见上面描述,需要修改的地方是两个,一个是instrumentation的tag,一个是Run As的Run Configuration,修改Instrumentation runner的值

2、调用机制:

三层封装:

Junit Report---》Robotium---》Instrumentation

四、脚本实现自动化后续收集工作

脚本1——运行testcase

脚本2——把xml从手机内存卡pull出来

脚本3——把xml转换成html

脚本4——把html的报告合并到手工、压力、性能报告中去

脚本5——发送邮件周知即可

Done!还是需要自己去写。。。

代码见下:

# -*-coding:gb2312-*-

#Function:界面程序,要去调用已经写好的重签名、install重签名后的apk和testapk,以及
#Author:LiXia
#Date:2015.01.16 #功能:要写界面程序 import wx
import os
import smtplib
import time
from xml.dom import minidom
import sys
from pyh import *
import webbrowser
import subprocess reload(sys)
sys.setdefaultencoding('gb2312') #获取时间戳
def timestamp(style = '%Y-%m-%d %H:%M:%S'):
return time.strftime(style, time.localtime()) ##mail_server请修改成自己公司的对应的正确的邮箱服务器,port也根据需要填写好
def sendMail(data, mail_from, mail_to, mail_title, mail_server=r'mail.x.y.net', mail_port=25):
try:
handle = smtplib.SMTP(mail_server, mail_port)
#handle = smtplib.SMTP('mail.360.cn', 25)
handle.ehlo()
handle.starttls()
handle.ehlo() mail_data = timestamp() mail_data = ""; msg = "From: %s\r\nTo: %s\r\nData: %s\r\nContent-Type: text/html;charset=gb2312\r\nSubject: %s \r\n\r\n %s\r\n" % (mail_from, str(mail_to), mail_data, mail_title, data)
handle.sendmail('%s' % mail_from, mail_to, msg)
handle.close()
return True
except Exception, e:
print e
logobj.error('发信失败,程序退出')
return False def XMLtoHTML():
# 读取xml文档内容
doc = minidom.parse('junit-report.xml') #跑成功的case,存一下
RunSuccess = []
#跑失败的case,存一下
RunFailure = [] _document = doc.documentElement
#testsuites与testcase之间不是一个父子的关系
testsuites = _document.getElementsByTagName('testsuite')
for testsuite in testsuites:
pass
# print testsuite
# print testsuite.nodeName
# print testsuite.nodeValue
# print testsuite.attributes['name'].value testcases = _document.getElementsByTagName('testcase')
i = 0
j = 0
for testcase in testcases:
print testcase.nodeName
print testcase.attributes['classname'].value
print testcase.attributes['name'].value
print testcase.attributes['time'].value classname = testcase.attributes['classname'].value.encode('gb2312')
name = testcase.attributes['name'].value.encode('gb2312')
time = testcase.attributes['time'].value.encode('gb2312') if testcase.firstChild != None:
print 'Run Failure'
failure = testcase.firstChild
print failure.nodeName
print failure.attributes['message'].value.encode('gb2312')
print failure.attributes['type'].value.encode('gb2312')
print failure.firstChild.nodeValue failureMessage = failure.attributes['message'].value.encode('gb2312')
failureType = failure.attributes['type'].value.encode('gb2312')
failureContent = failure.firstChild.nodeValue.encode('gb2312') # RunFailure.append({'testcase.classname': classname, 'testcase.name': name, 'testcase.time': time,
# 'failureMessage': failureMessage, 'failureType': failureType, 'failureContent': failureContent})
RunFailure.append([classname, name, time, failureMessage, failureType, failureContent]) else:
#说明没有failure的这一项,那就是运行成功了
print 'Run Success!!'
# RunSuccess.append({'testcase.classname': classname, 'testcase.name': name, 'testcase.time': time})
RunSuccess.append([classname, name, time]) print '以下是成功的case的基本信息'
for i in range(0, len(RunSuccess)):
print RunSuccess[i] #print '以下是失败的case的基本信息' #for j in range(0, len(RunFailure)):
# print RunFailure[j] #直接生成所需要的html文档
page = PyH('路由器卫士自动化测试Case运行结果') #显示一个汇总的表格,总共多少Case,成功多少,失败多少
page <<h2('Table of Run Cases')
mytab = page << table(border = "")
tr1 = mytab << tr()
tr1 << td('TestCase-AllNum')
tr1 << td('TestCase-SuccessNum')
tr1 << td('TestCase-FailureNum') numAll = len(RunSuccess) + len(RunFailure)
numSuccess = len(RunSuccess)
numFailure = len(RunFailure) tr2 = mytab << tr()
tr2 << td(numAll)
tr2 << td(numSuccess)
tr2 << td(numFailure) #TestCase 成功的
page << h2('Tabel of Run Success')
mytab = page << table(border = "")
tr1 = mytab << tr()
tr1 << td('TestCase-ClassName')
tr1 << td('TestCase-Name')
tr1 << td('TestCase-Time(S)') for i in range(len(RunSuccess)):
tr2 = mytab << tr()
for j in range(len(RunSuccess[i])):
tr2 << td(RunSuccess[i][j]) #TestCase 失败的
page << h2('Tabel of Run Failure')
mytab = page << table(border = "")
tr1 = mytab << tr()
tr1 << td('TestCase-ClassName')
tr1 << td('TestCase-Name')
tr1 << td('TestCase-Time(S)')
tr1 << td('TestCase-FailureMessage')
tr1 << td('TestCase-FailureType')
tr1 << td('TestCase-FailureContent') for i in range(len(RunFailure)):
tr2 = mytab << tr()
for j in range(len(RunFailure[i])):
tr2 << td(RunFailure[i][j]) page.printOut("caseResult.html")
# webbrowser.open("caseResult.html") #重签名的函数,是在cmd里面调的
def reSign(event):
cmd1 = r'java -jar re-sign.jar'
# os.system(cmd1)
subprocess.Popen(cmd1) def getPath2(event):
dirname = 'C:'
dialog = wx.FileDialog(None, "choose a file",dirname,"","*.*",wx.OPEN)
if dialog.ShowModal() == wx.ID_OK:
fileChoose2.SetValue(dialog.GetPath()) dialog.Destroy() def getPath3(event):
dirname = 'C:'
dialog = wx.FileDialog(None, "choose a file",dirname,"","*.*",wx.OPEN)
if dialog.ShowModal() == wx.ID_OK:
fileChoose3.SetValue(dialog.GetPath()) dialog.Destroy() #安装apk程序的函数,第二步和第三步一起来啦
def installApk2(event):
cmd1 = r'adb install' + ' ' + fileChoose2.GetValue()
# ret = os.popen(cmd1)
ret = subprocess.Popen(cmd1, stdout = subprocess.PIPE)
result = ret.stdout.read()
print '返回值:' + result
if ('Failure' in result):
print '失败啦!'
dlg = wx.MessageDialog(None, result, '安装失败', wx.YES_NO)
if dlg.ShowModal() == wx.ID_YES:
dlg.Close(True)
dlg.Destroy()
elif ('Success' in result):
print '成功啦!'
dlg = wx.MessageDialog(None, result, '安装成功', wx.YES_NO)
if dlg.ShowModal() == wx.ID_YES:
dlg.Close(True)
dlg.Destroy() def installApk3(event):
cmd2 = r'adb install' + ' ' + fileChoose3.GetValue()
# ret = os.popen(cmd2)
ret = subprocess.Popen(cmd2, stdout = subprocess.PIPE)
result = ret.stdout.read()
print '返回值:' + result
if ('Failure' in result):
print '失败啦!'
dlg = wx.MessageDialog(None, result, '安装失败', wx.YES_NO)
if dlg.ShowModal() == wx.ID_YES:
dlg.Close(True)
dlg.Destroy()
elif ('Success' in result):
print '成功啦!'
dlg = wx.MessageDialog(None, result, '安装成功', wx.YES_NO)
if dlg.ShowModal() == wx.ID_YES:
dlg.Close(True)
dlg.Destroy() #通过appt获取包名,看看行不行
def getPackageInfo(apkPath):
cmd1 = r'aapt.exe' + ' ' + r'dump badging' + ' ' + apkPath
ret = subprocess.Popen(cmd1, stdout = subprocess.PIPE)
result = ret.stdout.read()
print '返回值:' + result print '开始找index位置:\n'
indexPackageName1 = result.find("name")
indexPackageName2 = result.find("version")
indexMainActivity1 = result.find("launchable-activity")
indexMainActivity2 = result.find("label", result.find("launchable-activity")) print indexPackageName1,indexPackageName2,indexMainActivity1,indexMainActivity2 #然后根据这几项提取我需要的内容即可 #这里面应该用len(name='),但是因为有特殊字符‘,导致出现了问题,以后再想,反正这个是搞定了 packageName = ''
for i in range(indexPackageName1+6, indexPackageName2-2):
packageName += result[i] print packageName mainActivity = ''
for i in range(indexMainActivity1+27, indexMainActivity2-3):
mainActivity += result[i] print mainActivity return packageName #跑case啊,还是在cmd里面来的
def runCase(event):
#先用这个来吧,之后再说获取包名和走单个case的情况
# cmd1 = r'adb shell am instrument -w com.qihoo.router.test/com.zutubi.android.junitreport.JUnitReportTestRunner'
#传进来一个参数,这个参数是获取到的packageName,这样就不用写死了
packageName = getPackageInfo(fileChoose3.GetValue())
print packageName
cmd1 = r'adb shell am instrument -w' + r' ' + packageName + '/' + 'com.zutubi.android.junitreport.JUnitReportTestRunner'
print cmd1
# os.system(cmd1)
subprocess.Popen(cmd1) #pull结果啊,从手机端pull到PC端
def pullResult(event):
#后面不加路径的话,应该会直接pull到当前目录吧,试试
packageName = getPackageInfo(fileChoose2.GetValue())
cmd1 = r'adb pull /data/data/' + packageName + '/files/junit-report.xml'
print cmd1
# os.system(cmd1)
ret = subprocess.Popen(cmd1, stdout = subprocess.PIPE)
result = ret.stdout.read()
dlg = wx.MessageDialog(None, result, 'xml测试结果从手机pull到PC端', wx.YES_NO)
if dlg.ShowModal() == wx.ID_YES:
dlg.Close(True)
dlg.Destroy() #转换xml为html,并发送邮件
def sendHtmlResult(event):
XMLtoHTML()
f = open("caseResult.html")
contents = f.read() #这里的mail_to应该是一个列表,最开始的时候,直接写的就是字符串,它无法解析成功
#下面是错误写法:
#mail_to_error = "lixia-xy@360.cn, sunjingjing@360.cn"
#mail_to = ["lixia-xy@360.cn", "sunjingjing@360.cn", "jialiwei-s@360.cn", "mengmo@360.cn", "cuifang@360.cn"]
mail_to = ["xxx@yyy.com"] sendMail(contents, "xxx@yyy.com", mail_to, "路由器卫士自动化回归运行结果"); app = wx.App()
frame = wx.Frame(parent = None, id = -1, title = '安卓自动化测试', size = (870, 600))
bkg = wx.Panel(frame) #往里面添加所需要的控件
#第一步,重签名,选择需要重签名的apk(目前只能对非加固的包做处理)
#第二步,安装程序,重签名后的apk和自己生成的testapk,通过cmd
#第三步,开始运行测试,可以给出选项吧,case全跑,或者给出一个下拉列表,想跑哪些自动勾选,做成一个可多选的那个控件??
#第四步,收集测试结果,其实就是用cmd命令通过adb pull把生成的xml文件搞出来
#第五步,转换xml为html,并发送邮件??这个程序已经写好,到时候直接调就行 Info = wx.StaticText(bkg, -1, '请大家按照下面的步骤来,就能完成测试、结果收集以及邮件通知啦!!', pos = (40, 30)) stepOne = wx.StaticText(bkg, -1, '第一步:重签名', pos = (40, 80))
#fileChoose = wx.TextCtrl(bkg, -1, '选择你需要重签名的apk文件', size = (380, -1), pos = (200, 80))
#OKButton1 = wx.Button(bkg, -1, '重签名', pos = (600, 80))
OKButton1 = wx.Button(bkg, -1, '重签名', pos = (200, 75))
OKButton1.Bind(wx.EVT_BUTTON, reSign) stepTwo = wx.StaticText(bkg, -1, '第二步:安装待测程序', pos = (40, 150))
fileChoose2 = wx.TextCtrl(bkg, -1, '选择你需要测试的重签名后的apk文件', size = (380, -1), pos = (200, 150))
pathButton2 = wx.Button(bkg, -1, '选择文件', pos = (600, 145))
pathButton2.Bind(wx.EVT_BUTTON, getPath2)
OKButton2 = wx.Button(bkg, -1, '安装', pos = (700, 145))
OKButton2.Bind(wx.EVT_BUTTON, installApk2) stepThree = wx.StaticText(bkg, -1, '第三步:安装Test程序', pos = (40, 220))
fileChoose3 = wx.TextCtrl(bkg, -1, '选择你生成的testapk文件', size = (380, -1), pos = (200, 220))
pathButton3 = wx.Button(bkg, -1, '选择文件', pos = (600, 215))
pathButton3.Bind(wx.EVT_BUTTON, getPath3)
OKButton3 = wx.Button(bkg, -1, '安装', pos = (700, 215))
OKButton3.Bind(wx.EVT_BUTTON, installApk3) stepFour = wx.StaticText(bkg, -1, '第四步:开测', pos = (40, 290))
OKButton4 = wx.Button(bkg, -1, '全Case回归', pos = (200, 285))
OKButton4.Bind(wx.EVT_BUTTON, runCase) stepFive = wx.StaticText(bkg, -1, '第五步:收集测试结果', pos = (40, 360))
OKButton5 = wx.Button(bkg, -1, '获取测试xml结果', pos = (200, 355))
OKButton5.Bind(wx.EVT_BUTTON, pullResult) stepSix = wx.StaticText(bkg, -1, '第六步:回归结果周知', pos = (40, 430))
OKButton6 = wx.Button(bkg, -1, '发送邮件告诉大家啦!!', pos = (200, 425))
OKButton6.Bind(wx.EVT_BUTTON, sendHtmlResult) frame.Show()
app.MainLoop()

Android-Junit-Report测试报告生成——Android自动化测试学习历程的更多相关文章

  1. 百度Cafe原理--Android自动化测试学习历程

    主要讲解内容及笔记: 一.Cafe原理 Cafe是一款自动化测试框架,解决问题:跨进程测试.快速深度测试 官网:http://baiduqa.github.io/Cafe/ Cafe provides ...

  2. 截图原理(一)——Android自动化测试学习历程

    把两节的内容汇总起来,第一节讲的是如何在apk中直接进行截屏,用到了Robotium的Solo类的takeScreenShot方法,有一个小的demo,以及从方法一直往里钻,知道它具体是怎么进行截屏的 ...

  3. Instrumentation类——Android自动化测试学习历程

    这里需要把Instrumentation类的视频的上.中.下三集一起看,把内容总结一下... 视频地址: http://study.163.com/course/courseLearn.htm?cou ...

  4. Monkey原理初步和改良优化--Android自动化测试学习历程

    章节:自动化基础篇——Monkey原理初步和改良优化(第三讲) 主要讲解内容与笔记: 一.理论知识: 直接看文档,来了解monkey的概念.基本原理,以及如何使用. First,what is And ...

  5. 跨进程(同一app不同进程之间通信)——Android自动化测试学习历程

    视频地址:http://study.163.com/course/courseLearn.htm?courseId=712011#/learn/video?lessonId=877122&co ...

  6. 截图原理(二)——android自动化测试学习历程

    接上一篇(截图原理) 视频地址:http://study.163.com/course/courseLearn.htm?courseId=712011#/learn/video?lessonId=87 ...

  7. Selenium原理初步--Android自动化测试学习历程

    章节:自动化基础篇——Selenium原理初步(第五讲) 注:其实所有的东西都是应该先去用,但是工具基本都一样,底层都是用的最基础的内容实现的,测试应该做的是: (1)熟练使用工具,了解各个工具的利弊 ...

  8. 自动化预备知识上&&下--Android自动化测试学习历程

    章节:自动化基础篇——自动化预备知识上&&下 主要讲解内容及笔记: 一.需要具备的能力: 测试一年,编程一年,熟悉并掌握业界自动化测试工具(monkey--压力测试.monkeyrun ...

  9. Appium原理初步--Android自动化测试学习历程

    章节:自动化基础篇——Appium原理初步(第七讲) 本期关键词: Appium.跨语言跨平台.Bootstrap 主要讲解内容及笔记: 一.what is appium 一种封装了uiautomat ...

随机推荐

  1. 正确使用List.toArray()(转)

    在程序中,往往得到一个List, 程序要求对应赋值给一个array, 可以这样写程序: for example:   Long [] l = new Long[list.size()]; for(in ...

  2. 初识Winform , 还好没喜欢上控制台

    虽然没听的太懂, 不过还是写点东西吧. 我呢, 就跟着这本书写了个学生管理系统 前面刚会了SQLserver, 所以这个学生管理系统需要连上数据库, 毕竟学了不用天诛地灭 既然需要连接数据库, 就要用 ...

  3. lsm-tree

    https://www.quora.com/How-does-the-Log-Structured-Merge-Tree-work http://blog.cloudera.com/blog/2012 ...

  4. 使用as3控制动画的播放与暂停

    1.需要两个按钮元件 2.在属性面板为两个按钮元件分别命名为pausebutton与playButton 3.代码 stop(); pausebutton.visible = false; playB ...

  5. 全景视频外包团队:技术分享Unity3D全景漫游

    作者:未知 1.建模中使用的图片.文件.文件夹等以及模型中物体.材质等的名称都不能使用中文或者特殊符号,可以使用英文字母.数字.下划线等 2.调整Max的单位为米 3.烘培光影的设置 4.模型的中的植 ...

  6. PDO多种方式取得查询结果

    PDO多种方式取得查询结果 01 December 2009 1:26 Tuesday by Sjolzy PDO最大的特点之一是它的灵活性,本节将介绍如何取得查询结果,包括: 数组(数值或关联数组) ...

  7. lumen 登陆 注册 demo

    本文将用Lumen来实现一个完整的用户注册.登录及获取用户信息的API. Lumen环境搭建和初始化详细步骤请参考上篇文章<Lumen安装配置使用入门>一文. 一.准备工作 1.Lumen ...

  8. 51nod 最近刷题 简要题解

    51nod 1564 由于数据是随机的,可以证明,对于每一个数,向左或右找比它小的数,长度是logn级别的 考虑枚举最大值 注意,对于每一个最大值,如果直接用2个循环枚举左右端点的话,理论是lognl ...

  9. mysql数据库优化小结

    一.常见数据库的优化操作 1.表的设计要符合三范式. 2.添加适当的索引,索引对查询速度影响很大,必须添加索引.主键索引,唯一索引,普通索引,全文索引 3.添加适当存储过程,触发器,事务等. 4.读写 ...

  10. winform画图闪烁问题

    问题:在winform程序的onpaint方法中画图, 连续画, 如鼠标移动时就要不断画图, 会闪烁. 解决方法:将要画图的部分放到一个自定义控件中, 自定义控件的onpaint方法里面画图, 然后再 ...