Appium 使用 Appium+HTMLTestRunner 输出自动化测试报告时,增加报告中展示截图功能

奋斗的鸡腿 · 2017年02月09日 · 最后由 冷月醉夕阳 回复于 2017年09月07日 · 2780 次阅读

由于 HTMLTestRunner 没有展示截图的功能,所以在基于 UI 的自动化测试报告中不能展示截图,就让整个报告显得不直观,
整个解决方案的思路是就是执行自动化测试脚本时截图并按照一定规则命名保存,形成测试报告时也按照相同的规则取图片,思路很简单,废话少说,上代码:

1.先在 HTMLTestRunner 中增加一列 Screenshot,用于展示图片。

View后面追加一行Screenshot

2.在

%(error) s 后面追加一行  

3.在 REPORT_TEST_WITH_OUTPUT_TMPL 中增加一行

,用于图片展示

4.在 class HTMLTestRunner(Template_mixin) 中的 def _generate_report_test(self, rows, cid, tid, n, t, o, e): 中的 row 中增加 screenshot 字段,用于往上面步骤中传实际的图片地址,

5.然后增加一个获取 screenshot 地址的函数,由于截取图片和获取图片地址是异步进行,所以不得不进行轮询去查,10 秒查一次,最多查 10 次:

至此,HTMLTestRunner.py 文件修改完毕,然后就是在测试脚本中的注意事项了。

1.单独写一个 screenshot 函数,然后在 teardown 函数中调用,这样就可以保证每条用例都会执行到。
2.图片命名规则要跟 HTMLTestRunner 中的一致,不然取不到,我的命名规则 日期 +_+index.png index 根据截取的是第几张图片。

直接上测试用例脚本吧:
import os
import unittest
from appium import webdriver
from appium.webdriver.common.touch_action import TouchAction
from appium.webdriver.common.multi_action import MultiAction
from time import sleep
import time
import HTMLTestRunner
import sys

PATH = lambda p:os.path.abspath(
os.path.join(os.path.dirname(file),p)
)
reload(sys)
sys.setdefaultencoding('utf-8')
index = 1

class VodTest(unittest.TestCase):

png_file = 'E:\Testreport\png\'

def setUp(self):

desired_caps={}
desired_caps['platformName'] = 'Android'
desired_caps['platformVersion'] = '4.4.3'
desired_caps['deviceName'] = '192.168.1.100:5555'

desired_caps['appPackage'] = '**'
desired_caps['appActivity'] = '.
***
desired_caps['appWaitActivity'] ='.*******
desired_caps['noSign'] = 'True'

self.driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)
def screenshot(self,index):
timestr=time.strftime('%Y%m%d',time.localtime(time.time()))
img_name=timestr + '_' + str(index) +'.png'
self.driver.get_screenshot_as_file('%s%s' % (self.png_file,img_name))
return img_name

def tearDown(self):
global index
print index
self.screenshot(index)
index+=1
print 'index2' + str(index)
self.driver.quit()
def test_head(self):
sleep(10)
self.driver.wait_activity('.MasterViewActivity',15,5)
self.driver.find_element_by_android_uiautomator("text(\"搜索\")")

self.driver.find_element_by_id('com.jamdeo.tv.vod:id/ib_search')
self.driver.find_element_by_android_uiautomator("text(\"历史/收藏\")")

self.driver.find_element_by_id('com.jamdeo.tv.vod:id/iv_play_history_and_my_favorite')
self.driver.find_element_by_android_uiautomator("text(\"微信电视\")")
self.driver.find_element_by_android_uiautomator("text(\"设 o 置\")")

def test_hidefirstline(self):
sleep(10)
self.driver.wait_activity('.MasterViewActivity', 15, 5)

self.driver.find_element_by_android_uiautomator("text(\"按上键展开分类\")")

self.driver.find_element_by_android_uiautomator("text(\"闻香识女人\")")

self.driver.find_element_by_id('com.jamdeo.tv.vod:id/iv_poster')
self.driver.find_element_by_id('com.jamdeo.tv.vod:id/iv_mark_card')
self.driver.find_element_by_android_uiautomator("text(\"复仇者联盟 2:奥创纪元\")")

self.driver.keyevent(19)

self.driver.find_element_by_id('com.jamdeo.tv.vod:id/iv_poster')

if name == 'main':

suite = unittest.TestSuite()
suite.addTest(VodTest('test_head'))
suite.addTest(VodTest('test_hidefirstline'))

timestr = time.strftime('%Y%m%d%H%M%S',time.localtime(time.time()))
filename = 'E:\Testreport\'+timestr+'.html'
print filename
fp = open(filename, 'wb')
runner = HTMLTestRunner.HTMLTestRunner(

stream=fp,
title='result',
description='report'
)

runner.run(suite)
fp.close()

不好意思 有点乱 大家将就着看吧

大家有没有什么更好的方法 欢迎交流
最后输出的测试报告大概就是酱紫!:

github:https://github.com/342164796/HTMLTestRunner.py

共收到 48 条回复 时间 点赞

点个赞 。顺便请教下,webview 能操作截图不,我原生的可以截图,webview 报错:selenium.common.exceptions.WebDriverException: Message: unknown error: cannot activate web view

#1 楼 @hukui51770 没有尝试过,我用的是 appium 里的截图方法,如果不行的话就自己写一个截图的方法,到时候调用就可以吧。

赞,代码格式可以调整一下

点赞 正好需要 学习一下

可以帮忙看下这个错误吗,谢啦

#5 楼 @jungle 你要先定义一下你这个 img_path 的路径啊 这个路径就是你执行自动化测试脚本时截取图片的存放地址。
类似于下面:

#6 楼 @342164796 恩,谢谢,现在运行完 E:\Testreport\png\这个路径下有截图,但是测试报告没有获取到,是哪儿的路径写错了吗

#7 楼 @jungle 你点击一下 detail 里面应该有吧~
http://pan.baidu.com/s/1i48Pw9J
这个是我的 自己参考下吧 你不给我看你代码 我也不好说

#8 楼 @342164796 ,Detail 显示的是 pass,我对比咱俩的代码,没什么不一样的地方,你可以告诉我你 QQ 吗,我发给你帮我看下

#9 楼 @jungle 发这个邮箱吧 我们公司上不了 QQ

#10 楼 @342164796 谢谢啦,不过我知道原因了,我用火狐打开图片就不显示,用 360 浏览器就可以,脚本没问题,太感谢你了~

用例 1 有截图 用例 2 没截图 用例 3 有截图 -》生成的报告 用例 2 显示用例 3 的截图,用例 3 的地方没有截图。

#12 楼 @ctro15547 当时没有考虑这么多,默认情况下,在执行测试脚本时,要求的是每个测试用例都执行下截图。 因为测试和生成报告时异步执行的,这个地方不大好实现 ,你可以根据你的需要来修改下~ 如果实现了,大家交流下~

#13 楼 @342164796 我也是小白 ,暂时只好每个用例都加上截图来规避一下这个问题了~ 最后面每 10 秒查询 10 次这个没搞懂是什么意思,3 条用例执行了 200 多秒。。

#14 楼 @ctro15547 哈哈 因为是异步执行,我是担心图没有截取完毕这儿就生成测试报告了 这个时候图就找不到,所以加了个判断,你可以根据自己的需要适当缩减一下时间~ 如果有更好的方法 欢迎沟通。

我可以完整截图了,但是代码看不是很懂,在 HTMLTestRunner 文件中的 screenshot 方法和测试脚本的 screenshot 各有什么作用呢? 脚本上的代码是对截图进行命名,然后 HTMLTestRunner 中的 screenshot 好像也是干的这件事?

我提供一下我的截图思路(Macaca + HTMLTestRunner):
1、在元素未找到时截图
2、在用例失败时截图

代码如下:
1、每个测试步骤加上@teststep这个装饰器
2、每条用例加上@testcase这个装饰器

def _screenshot(name):
    date_time = time.strftime('%Y%m%d%H%M%S', time.localtime(time.time()))
    screenshot = name + '-' + date_time + '.PNG'
    path = ReportPath().get_path() + '\\' + screenshot

    driver = BasePage().get_driver()
    driver.save_screenshot(path)

    return screenshot

def teststep(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        try:
            log.i('\t--> %s', func.__qualname__)
            ret = func(*args, **kwargs)
            return ret
        except WebDriverException:
            log.e('\t<-- %s, %s', func.__qualname__, 'Error')
            raise WebDriverException(message=flag + _screenshot(func.__qualname__))

    return wrapper

def testcase(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        try:
            log.i('--> %s', func.__qualname__)
            ret = func(*args, **kwargs)
            log.i('<-- %s, %s\n', func.__qualname__, 'Success')
            return ret
        except WebDriverException:
            log.e('<-- %s, %s\n', func.__qualname__, 'Error')
            raise WebDriverException
        except AssertionError:
            log.e('<-- %s, %s\n', func.__qualname__, 'Fail')
            raise AssertionError(flag + _screenshot(func.__qualname__))

    return wrapper

测试步骤的代码如下:

@teststep
def input_account(self, account):
    """以“请输入手机号码”的TEXT为依据"""
    self.driver\
        .element_by_name('请输入手机号码')\
        .clear()\
        .send_keys(account)

测试用例的代码如下:

@testcase
def test_Car_MyCarInsurEntry_Func_010(self):
    """我的车险入口验证"""
    self.home_page.click_my()

    login = LoginPage()
    if login.wait_page():
        login.input_account(VALID_ACCOUNT.account())
        login.input_password(VALID_ACCOUNT.password())
        login.login()

        gesture = GesturePasswordPage()
        if gesture.wait_page():
            gesture.skip()

        if self.home_page.wait_page():
            self.home_page.click_my()

    my_page = PlatformAppMyPage()
    my_page.wait_page()
    my_page.click_my_car_insurance()

    my_car_insurance = MyCarInsurancePage()
    self.assertTrue(my_car_insurance.wait_page())

#17 楼 @hualin 太棒了 我当时就是因为不知道怎样判断失败才搞成所有的用例执行完毕都要截图, 你这个更厉害一些 只有失败了才截图 还不太会用装饰器 。

#18 楼 @342164796 我在装饰器同时加了打印的,效果不错,可以用这种方式重构来看看

#19 楼 @hualin 恩 正在重构 搞成多线程的了 可以同时操控多台设备。 正在写 还没有特别好的思路。

#20 楼 @342164796 差不多,我弄的多进程,效果还可以。有需要可以一起探讨

#17 楼 @hualin 可以提供一下完整的代码吗?小白很多地方看不太懂。

#23 楼 @cynic 上面有个百度盘的链接 可以看下。

#24 楼 @342164796 指定一个文件夹存放截图 ,这样会把之前报告的截图照片给覆盖掉。但是文件精确到分秒后 又容易出错,报告获取不到文件名。@hualin的方法看不太懂,编程基础不行啊。。。。。。

#25 楼 @cynic 嗯是有这个问题,命名规则这个地方是有问题,我当时没有考虑那么多,觉得一天执行一次应该可以吧,你可以想个更好的命名规则,但是一定要个 html test 中的一致,不然取不到。

#25 楼 @cynic _screenshot 这个方法中有这样一小段 path = ReportPath().get_path() ,这个 path 就是每个设备在跑用例的时候用来放测试报告、日志、截图的路径。图片名加上时间是可以解决覆盖的问题的。

#27 楼 @hualin 这个 ReportPath() 里是怎么写的呢,可以提供一下整个工程的源文件吗?菜鸟对编程不是特别精通,只能看别人的优秀代码来提高。 HTMLTestRunner.py 文件的修改和@342164796 的应该也不太一样吧?

#28 楼 @cynic 在老家,没带电脑回来,下周一再细聊具体实现

#29 楼 @hualin 方便的话可以把源码发邮箱吗?一直在自学自动化的相关知识,论坛里真是卧虎藏龙啊

—— 来自 TesterHome 官方 安卓客户端

有个问题请教,我看楼主这个是只写了一个 py 一个 class,怎么能生成多个 py 的报告呢,比如多个 py 文件 多个 class 多个 testmethod,怎么引用 HTMLTestRunner

楼主有时间可以帮忙看一下咩~不知道是哪里出了问题~
打印出来的 screenshot 就是保存截图的路径,但是报告中没有显示图片信息

请问我为什么会报这个错呢

卡卡 回复

Index 用之前先定义一下,index=1 放在函数外面

请问 为什么我这生成的报告都是空白的,麻烦加个 QQ:1804882096 我发代码请你帮我看一下

麻烦问一下 print 'index2' + str(index),这句是干嘛的

卡卡 回复

没什么实际用,查看 index 是多少用的

我替换了你网盘的 HTMLTestRunner 文件,报告中没有出现,再加上我用例,报告中没有出现 img 列,可以帮我看看我的用例嘛

请问这句 PATH = lambda p:os.path.abspath(
os.path.join(os.path.dirname(file),p)
) 是什么意思,file 指什么

卡卡 回复

明早第一时间帮你看啊,今天一天太忙了,不好意思啊

好的,谢谢你

为什么我的图片不显示出来,

rabbit 回复

哥们,我也出现那个目录下面有截图,报告没有,不知道哪里出问题了,能不能帮下忙

86guoke 回复

不好意思,刚看到,你换个浏览器试试

@342164796 楼主,我按照你的 HTMLTestRunner.py 进行配置,但是在 eclipse 中 png file 这行却报错了,我刚刚接触 appium+python,请帮忙看一下,问题出在哪里?

这里都是满满的正能量,想法挺不错的,给个赞

49楼 已删除
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册