Android-Junit-Report測試報告生成——Android自動化測試學習歷程


視頻地址:

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 = "1")
    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 = "1")
    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 = "1")
    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()

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM