最近使用 appium+python 来做自动化,测试报告用的网上共享的 HTMLTestRunner 模块,但是里面貌似没有展示截图的功能,于是自己动手稍微修改了一下!
先上成果图,点击图片名字就能打开图片

总体思路为:在 HTMLTestRunner 的 report 模板中加入一列 picture,然后将用来展示图片的 html 打印到每个 case 的日志中,再从日志中将这段文字截取出来放入 report 中(主要是为了简单粗暴的将每个 case 与相关截图正确对应起来~想不到其他办法啦!欢迎高手们给出更加简单快捷的办法~)
以下为我的操作方法,有点长~
一、在 HTMLTestRunner.py 里的原有的 html 表格中插入了一列 picture(在

View下面加入一行Picture,在 下加入一行即可),用来展示脚本中的截图超链接
<tr id='header_row'>
    <td>Test Group/Test case</td>
    <td>Count</td>
    <td>Pass</td>
    <td>Fail</td>
    <td>Error</td>
    <td>View</td>
    <td>Picture</td>
</tr>
%(test_list)s
<tr id='total_row'>
    <td>Total</td>
    <td>%(count)s</td>
    <td>%(Pass)s</td>
    <td>%(fail)s</td>
    <td>%(error)s</td>
    <td>&nbsp;</td>
    <td><a href="" target="_blank"></a></td>
</tr>
</table>

二、还是在 HTMLTestRunner.py 里插入一行%(html) s 来放置对应脚本的截图

    REPORT_TEST_WITH_OUTPUT_TMPL = r"""
<tr id='%(tid)s' class='%(Class)s'>
    <td class='%(style)s'><div class='testcase'>%(desc)s</div></td>
    <td colspan='5' align='center'>

    <!--css div popup start-->
    <a class="popup_link" onfocus='this.blur();' href="javascript:showTestDetail('div_%(tid)s')" >
        %(status)s</a>

    <div id='div_%(tid)s' class="popup_window">
        <div style='text-align: right; color:red;cursor:pointer'>
        <a onfocus='this.blur();' onclick="document.getElementById('div_%(tid)s').style.display = 'none' " >
           [x]</a>
        </div>
        <pre>
        %(script)s
        </pre>
    </div>
    <!--css div popup end-->

    </td>
    %(html)s
</tr>

三、依然在 HTMLTestRunner.py 中,_generate_report_test() 函数中,插入 html 的内容,因为本身初学者,对 unittest 和 htmltestrunner 都不怎么了解,观察 htmltestrunner 的报告发现它能展示脚本执行过程中打印的 log 文本,所以我强行把自己组装的用于展示图片 html 代码打印到执行日志中,然后通过关键字截取出来,放置在 script 后面的位置,也就是刚才新建的 picture 列(要是觉得最后 report 中打印的 log 中加入了这段 html 影响美观,可以在下面代码中 script 里删掉~我懒得写了~)

script = self.REPORT_TEST_OUTPUT_TMPL % dict(
    id = tid,
    output = saxutils.escape(uo+ue),
)

s = uo+ue
html = s[s.find('htmlbegin')+9:s.find('htmlend')]

row = tmpl % dict(
    tid = tid,
    Class = (n == 0 and 'hiddenRow' or 'none'),
    style = n == 2 and 'errorCase' or (n == 1 and 'failCase' or 'none'),
    desc = desc,
    script = script,
    html = html,
    status = self.STATUS[n],
)

四、在自己封装的手机截屏方法中加入分别将截图名称和截图存放路径插入对应的数组中(也可以使用二维数组),以下分别为打印日志,截屏,组装 html 的方法,在组装完成的 html 代码开头和结尾分别叫上 htmlbegin 和 htmlend 作为之后截取使用的关键字

# _*_ coding:utf-8 _*_
__author__ = 'gjj14453'
import logging
import os
import common.myDate as myDate


PATH = lambda p: os.path.abspath(
    os.path.join(os.path.dirname(__file__), p))


pics = []
picpath = []


##################################
#  日志
# 0: debug
# 1:info
# 2:warning
# -1:error
###################################
def mylogger(msg, flag=1):
    logging.basicConfig(level=logging.INFO,
                    format='%(asctime)s %(filename)s %(levelname)s %(message)s',
                    datefmt='%a, %d %b %Y %H:%M:%S',
                    filename='mylog.log',
                    filemode='w')
    console = logging.StreamHandler()
    console.setLevel(logging.DEBUG)
    formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s')
    console.setFormatter(formatter)
    logging.getLogger('').addHandler(console)
    if flag == 0:
        logging.debug(msg)
    elif flag == 1:
        logging.info(msg)
    elif flag == 2:
        logging.warning(msg)
        screenshot()
    elif flag == -1:
        logging.error(msg)
        screenshot()
    logging.getLogger('').removeHandler(console)


#  截屏
def screenshot():
    dirname = PATH('D:\\MyConfiguration\\gjj14453\\PycharmProjects\\untitled\\list_out' + "/screenshot")
    os.popen("adb wait-for-device")
    os.popen("adb shell screencap -p /data/local/tmp/tmp.png")
    if not os.path.isdir(dirname):
        os.makedirs(dirname)
    pic = myDate.timestampname() + ".png"
    path = dirname + "/" + pic
    os.popen("adb pull /data/local/tmp/tmp.png " + PATH(path))
    os.popen("adb shell rm /data/local/tmp/tmp.png")
    mylogger("截图为:" + pic)
    pics.append(pic)
    picpath.append(path)


def creathtml(path, pic):
    html = ''
    if range(len(path)) > 0:
        for i in range(len(path)):
            if i == 0:
                html = '<a href=' + path[i] + ' target="_blank">' + pic[i] + '</a>'
            else:
                html = html + '<br /><a href=' + path[i] + ' target="_blank">' + pic[i] + '</a>'
    else:
        html = ''
    htmls = 'htmlbegin<td>' + html +'</td>htmlend'
    return htmls

五、之后在 case 中加入打印 html 到日志中的语句就 OK 啦

class Test(unittest.TestCase):

    def setUp(self):
        log.picpath = []
        log.pics = []
        pass

    def tearDown(self):
        pass

    def test(self):
        log.mylogger('aaa', -1)
        log.mylogger('bbb', -1)
        log.mylogger(log.creathtml(log.picpath, log.pics))

最后,还是希望有高手能给我一个更加简单的插入对应截图的办法~本来想把截图存放的数组传到脚本执行的 result 中再一一提取,试了几次毫无成果~


↙↙↙阅读原文可查看相关链接,并与作者交流