Appium [开源分享] 基于 appium 的自动化测试工具,支持多进程,性能采集分析等

joko · 2016年11月27日 · 最后由 Heroman 回复于 2021年11月12日 · 6415 次阅读
本帖已被设为精华帖!

简介

基于 appium 编写的自动化测试工具。使用方法简单,编写 yaml 文件格式的测试用例即可,无需改动任何一行代码。支持 Android,多台设备并行,性能采集等。
开源地址:https://github.com/ztwo/Auto_Analysis

为什么要重复造轮子

  • 稳定:执行过程中会被异常打断,目前已经捕获这些异常,增加了判断
  • 直观:例如性能报告有横向对比,错误日志筛选等
  • 简单:无需改动一行代码即可运行测试
  • 扩展:代码封装清晰,比较容易扩展,增加解释器

环境要求

  • macOS,linux,windows
  • appium 1.5.0+
  • python 2.7

工具特性

执行编写 yaml 格式的 testcase,执行后即可得到测试报告

  • 1:支持 Android 4.2.2 以上
  • 2:支持多设备并行测试
  • 3:性能采集与横向对比,每个 case 均是独立结果。
  • 4:支持 log 采集与清洗
  • 5:对 appium 异常的一些封装,例如失败重试,需要点击才能安装等
  • 6:用例编写支持继承多重继承,大部分用例仅需写两个步骤即可

快速开始

执行效果

12

报告样式

20161123810772016-11-23pm.png

测试用例编写规范

  • 1: 需要了解 yaml 格式编写规范,建议使用 pycharm 编写,自带 yaml 文档检查器. yaml 语法学习地址
  • 2: 用例名不可重复,会影响用例的继承

测试用例字段解释

字段 解释 演示 包含字段 是否必须
test_name 用例名 login /
test_id 用例 id 0001 /
test_control_type 查找控件方式 xapth xpath, id
test_action 操作方法 click 见下表
test_control 控件 com.xx.id /
test_text 断言、输入文本 test /
test_inherit 继承用例名 login /
test_range 循环本步骤次数 2 /
test_sleep 步骤执行后,等待秒 2 /
test_wait 配合断言,等待控件秒 30 /
test_action 解释 所有字段 配合字段 辅助配合字段
click 点击 click test_control_type,test_control /
send_keys 发送文本 send_keys test_control_type,test_control,test_text /
swipe 滑动 swipe_left,swipe_right,swipe_up,swipe_down / /
assert 断言 assert test_control_type,test_control,test_text test_wait
entity 实体按键 entity_home,entity_back,entity_menu,entity_volume_up,entity_volume_down / /

完整用例范例,用例名:login

---
-
  test_name: 点击跳过
  test_id: 0001
  test_control_type: id
  test_action: click
  test_control: test.joko.com.myapplication:id/button1
-
  test_name: 输入帐号名
  test_id: 0002
  test_control_type: id
  test_action: send_keys
  test_control: test.joko.com.myapplication:id/editText
  test_text: 199999999
-
  test_name: 输入密码
  test_id: 0003
  test_control_type: id
  test_action: send_keys
  test_control: test.joko.com.myapplication:id/editText2
  test_text: 9999

-
  test_name: 点击登录
  test_id: 0004
  test_control_type: xpath
  test_action: click
  test_control: //android.widget.Button[contains(@text,'确定')]

-
  test_name: 向上滑动页面
  test_id: 0005
  test_action: swipe_up
  test_range: 3

-
  test_name: 向下滑动页面
  test_id: 0006
  test_action: swipe_down
  test_range: 3


主要代码分析

  • 原理:开启 appium driver,解析 yaml 格式 testcase,执行,输出报告

  • po.BasePage:封装 appium driver 方法


def send_key_event(self,arg):
    """
    操作实体按键
    :return:
    """
    event_list = {'entity_home':3,'entity_back':4,'entity_menu':82,'entity_volume_up':24,'entity_volume_down':25}
    if arg in event_list:
        self.driver.keyevent(int(event_list[arg]))

  • po.ExecuteCase:执行 case 类

def get_all_case(self, path_yaml):
    """

    :param path_yaml: 用例地址
    :return: 返回yaml内字典,且遍历继承的信息,支持多重继承
    """

    def get_case(path_yaml):
        case_list = []

        inherit_case_file = public.GetCase.case_yaml_file()
        with open(path_yaml) as f:
            for dic in yaml.load(f):
                if isinstance(dic, dict):
                    if 'test_inherit' in dic:
                        inherit_case_name = dic['test_inherit']
                        inherit_case = inherit_case_name + '.yaml'
                        if inherit_case in inherit_case_file.keys():
                            case_list += case_list + get_case(inherit_case_file[inherit_case])

                    else:
                        case_list.append(dic)
                else:
                    U.Logging.warn('get_case:not dic')
        return case_list

    return get_case(path_yaml)


def __analysis_yaml(self, path_yaml):
    """
    测试用例解释器
    :param path_yaml: 测试用例地址
    1:每执行一条用例会记录下当前的性能


    :return:
    """


  • po.integration:初始化环境
  • lib.ScreenShot:minicap 截图,用于 appium screen 报错后,调用 minicap
def phone_screen(self, width_height, filename):
    """
    截图
    :param width_height: 宽高
    :param filename: 存储的文件名
    :return:
    """

    U.Logging.info('phone_screen:%s' % width_height)
    self.adb.shell(
        "'LD_LIBRARY_PATH=/data/local/tmp /data/local/tmp/minicap -P {}/0 -s > /data/local/tmp/{}.png'".format(
            width_height, filename))
    U.Logging.info('phone_screen:success')

  • lib.adbUtils:adb 方法封装,几乎封装了所有常用的方法。(此处感谢 testerhome 某位朋友,是基于他的开源脚本扩写的,有知道的是谁的帮忙 @ 下)
def last_update_time(self):
    """
    查询当前屏幕应用安装更新时间
    """
    for package in self.shell(
        'dumpsys package %s' %
            self.get_current_package_name()).stdout.readlines():
        if 'lastUpdateTime' in package:
            return package.split('=', 2)[1].strip()

def wifi_name(self):
    """
    查询连接wifi名称
    """
    for package in self.shell('dumpsys wifi').stdout.readlines():
        if package.startswith('mWifiInfo'):
            wifi_name = re.findall(r'SSID:([^"]+), BSSID', package)
            if not wifi_name:
                return None
            else:
                return wifi_name[0].strip()

  • lib.Utils:封装的一些基础方法,如创建数据库,log 输出等

  • public.installApp:安装应用,且同时开启线程,监控屏幕是否有需要点击的安装按钮


def main(self):
    """
    开启多线程:
            线程1:安装应用
            线程2:获取当前页面是否有可点击的按钮
    :return:
    """
    ini = U.ConfigIni()
    install_file = ini.get_ini('test_install_path', 'path')
    package_name = ini.get_ini('test_package_name', 'package_name')

    threads = []

    click_button = threading.Thread(target=self.tap_all, args=())
    threads.append(click_button)
    install_app = threading.Thread(
        target=self.__install_app, args=(
            package_name, install_file))
    threads.append(install_app)
    process_list = range(len(threads))

    for i in process_list:
        threads[i].start()
    for i in process_list:
        threads[i].join()

    self.adb.shell('"rm -r /data/local/tmp/*.xml"')

目前缺点:

  • 1.目前不支持 iOS,不支持 H5,不过框架是基于 appium,增加解释器即可
  • 2.测试报告样式略丑,因为样式是纯 html,不会 js,得慢慢改

正在完善

  • 1.对于执行过程中异常的梳理,例如低电量,其实已经实现,但会影响执行设备的性能。还需要再打磨
  • 2.错误信息的完善

开源地址:https://github.com/ztwo/Auto_Analysis

共收到 189 条回复 时间 点赞
张小强 回复

请问这个问题后来如何解决的?

楼主,vivo 手机用 adb 安装的时候,需要确认才能安装,你在 adb 安装的时候,调用 self.uidump() 获取页面的 xml 文件,但是 adb install 的时候用 adb shell uiautomator dump 是不能同时使用的,所以无法获取到安装确认页面,一直提示 “2019-08-15 15:34:33 [Success]:installApp:element, return:None”,所以一直装不成功


还想教下楼主,button5 = 'vivo:id/vivo_adb_install_ok_button'这个 button 的 id 是如何获取的呀?

王志国 回复

我遇到和你一样的问题,请问你解决了吗?

楼主,我写的动态申请权限自动点击,点击完第一个允许后,点击第二个允许就一直不执行,没收到这个命令,不知为什么

@joko 楼主好,我这边遇到用例中的测试步骤总是执行完第一个就不会再往下执行的情况,比如截图中的首页列表页滑动 2 用例执行完后就不再执行首页列表页滑动 3 了,麻烦帮忙看下。我使用的是我们自己的 app

你好,请问 yaml 里面怎么设置多个检查点?

楼主,能留个联系方式嘛。。。有点问题请教

@joko 不知道你还会不会回帖,我遇到了这个问题。运行时直接卡在这里不运行了,有时候可以运行成功但是会一直提示找不到 dump.xml 这个文件



这些文件夹只有关闭运行的时候才能生成,并不是实时生成的,不知道什么原因

万分感谢啊,楼主,哈哈哈

111 回复

你这个 198 楼已经给出方法了


楼主你好,我被这个问题困扰好久了,找答案也没找到可靠的,请问你有解决的办法吗😭

205楼 已删除

@joko @harsayer 代码写的确实厉害,看着也是一种享受!同楼上,求继续维护。

@joko 看了你的源码,真乃大神也, 进程 线程 装饰器 形参 位参。。。。 adb 封装一句搞定。
logging 使用出神入化。。。 代码规范完全符合 google 工程级标准。
求继续维护,支持 h5 支持 ios 和 支持设备管理 等其它功能

老马 python appium UI 自动化测试框架讨论 中提及了此贴 02月01日 10:48

你好,请问这个抓取的性能指标是通过什么接口做到的,能都详细介绍下

joko #200 · 2017年09月27日 Author
jierong01 回复

没有最好,适合自己的场景才是好的。一个步骤采集一次已经可以满足大部分场景了

joko 回复

有个疑问:获取性能数据,是每一个步骤获取一次??还是我设置一个循环,让他间隔一段时间来获取比较好尼??

joko #198 · 2017年09月27日 Author
jierong01 回复

是每一个步骤获取一次,每次获取一个数值,加 sleep 并不会获取累计的数据,没什么影响。不过别这么做,操作后立即获取得到的数据比较能反应性能状态,等待 1 秒后,可能已经回落

hello,还是我,看了你的源码,发现你获取性能指标类似 cpu,men 是每执行一个 action,就是一个动作才去获取一次的,是这样吗,如果改成 sleep(1)然后获取,然后累计的数据量可能比较大,这样生成性能的折线图片是否会有问题尼??

jierong01 回复

感谢,问题已经找到,我把 Utils.py 里面的 cmd 方法中的 close_fds=True,改成了 False,可以跑起来了,不过不知道会不会有什么不好的影响


@joko 请问一下,没有跑起来,出现了这个异常

194楼 已删除

@joko 最近在看你的框架,发现多设备并行执行的时候,怎么处理不同的账号登录类似这样的场景??

小王子 回复


因为 adb shell dumpsys display | grep PhysicalDisplayInf 出来的数据为空,我也遇到这个问题,把 PhysicalDisplayInf 改为 DisplayDeviceInfo 就 ok 了

找到原因了,是因为我有 appium 的快捷方式,代码里执行 appium 命令的时候会把客户端启动,但是不是 launch server 导致程序无法继续。后来删了快捷方式,命令执行就正确了。

@joko 请教下,我执行 demo_run.py,server 不会自动启动,而且我手动启动 server 后,python 脚本也一直不执行,什么输出都没有。谢谢了呢

189楼 已删除
joko 回复

好的十分感谢

joko #187 · 2017年05月17日 Author
LambooZ 回复

卡住的问题我得查查,这块之前为了避免 windwos 一个 bug,特意这么实现的。如果方便你把日志发我邮箱

joko #186 · 2017年05月17日 Author
深蓝 回复

维护,端午节有时间我更新一下,class name 的实现方式。卡住的问题你方便的话我日志发我邮箱,邮箱在 setup.py 里

joko #185 · 2017年05月17日 Author
龙十 回复

没限制连接数啊

您好,请问这里面比如有 3 个相同 id 的控件,这种以数组形式存在的 id 如何输入呢

请问您现在还在维护吗

你好,运行过程中经常会卡住,没有报错,没有超时,比如上一步骤执行成功之后,然后就没有日志输出,感觉像终端都一起卡死了一样不清楚什么问题,关闭终端重新打开又不卡了,或者又在其他步骤后面不动了,这种可能是什么原因呀

@joko 为什么最多同时连接 6 个手机?有这个限制的作用是?

@xiaocong168 请问你 #61 的错误是怎么解决的

请问 #61 的问题怎么解决?appium 不能自动启动

joko #178 · 2017年04月17日 Author
Kz 回复

目前不支持,你改造一下即可,加一些 test_action

Jason1227 回复

selenium 新版问题,退回老版本即可:https://github.com/appium/python-client/issues/162

@joko 请问大大这个报错是怎么回事?如何解决 谢谢

你好,本人小白 请问有长按、双击、点击拖拽这些 test_action 吗?

@joko

问题:

  • 是否支持模拟器?
  • result 内无 test_report 报告~
  • 问题截图:

173楼 已删除

问题 1:你说的找不到控件指的是:业务条件不允许使用以控件断言,还是因为业务的变动,此控件不存在?第一种:目前只支持控制属性断言,如果想增加新的可以在 ExecuteCase.py 内增加方法。第二种:如果相关控件的消失,或者控件文本变化,用例会执行失败的

问题 2:问题我在上一个回复回答过了。用例的操作不能作为成功失败的验证,验证需要断言。举个实际例子:1:输入帐号,2:输入密码,3:登录,4:验证登录页面。操作和断言是两个事情。不过如果你确实有操作失败就报错的需要,可以改下代码:BasePage.py->find_element,把这个函数的 try 去掉即可

joko 回复

方便加个 QQ 或者微信吗 @joko

170楼 已删除
joko #169 · 2017年03月24日 Author

不能用操作层来判断用例的状态,需要用断言来验证,一个完整的用例是需要有断言存在。

168楼 已删除

请问你是用的什么工具共享的屏幕呀?啊

请问你是用的什么工具共享的屏幕呀?

joko #165 · 2017年03月24日 Author

你好,你的问题是装饰器捕获了异常,但在报告结果内未展示异常显示的是 true?

164楼 已删除

@121miao 应该不是 bug,作者用了一个修饰器来捕获异常,默认作者返回了 true,当不出现任何异常的时候,当遍历 yaml 文件中每一步操作时,如果遇到异常或者 AssertionError 和 AttributeError 时会捕获到,并返回出去。
作者只是给了一个整体框架,1 个月前我还从未学过 python,就在作者的这套框架上边学边做边改,带着问题去查,能学到不少东西。

FFMS 回复

你自己加载一下这个模块就知道了

龙十 回复

这个模块是楼主的代码库中安装的 ? 还是 python 内置?

160楼 已删除

@FFMS 根据提示啊 没有找到 ConfigParser 模块

同样实现了 js 版本的自动化测试工具 sptt,设计理念和本文大同小异,对此有兴趣的同学可以联系我哈

@joke @yxrs89 帮看下这是什么原因引起的?

@121miao 他用的字典,你改为 OrderedDict 有序字典,至少就可以按字母顺序执行了。具体需求你可以根据你所需要的执行顺序更改 GetFilePath.py 这个文件哈

155楼 已删除

@121miao 多台手机一起执行啊

153楼 已删除

马克一个 感谢楼主啊

eddyyzx 回复

我最近也遇到了这个问题,请问有知道怎么写吗? 感谢回复

@joke 好的,谢谢啦,我周末已经改造好了。用起来还是很方便的

paul 回复

你好,改造 GetFilePath.py 内的函数即可实现。没现实顺序执行的原因:遍历的路径加入字典后,变为了无序状态,所以未顺序执行,你可以改造下这个函数

@joko joke 兄想请问下,如果在你现在代码基础上,什么不做修改,如果我 testcase 建了多个目录,目录中有多个 case 那么我的执行顺序什么样的,遍历顺序按字母顺序来还是什么。不怕见笑我对 python 刚入门,有的还不能完全看懂。。

joko 回复

@joko 谢谢 joke 兄,不过你能把框架开源给大家已经十分受尊敬。支持你!

joko #146 · 2017年02月22日 Author

#147 楼 @yxrs89 顺序执行和取消执行,目前均没有控制,需要加的逻辑太多。最近在加其他方面,后期会增加选择方便的控制台

楼主大大你好,想问下,如果我所有的 testcase 全写好了,但是运行的时候有部分 case 不想执行应该如何操作,你的执行顺序如何控制,目前只是通过继承关系。

144楼 已删除

楼主你好,请问 case 执行的顺序是怎么样的? 现在有个场景是这样的,我执行了部分 case 之后,其中一条 case 是退出当前的 app,需要重启 app?

#142 楼 @joko 谢谢回复,已经解决了,是我一个 activity 写错了

#142 楼 @joko 1080P 的分辨率,公司自研机😀

#141 楼 @chenxin appium 无法启动,看下你配置的参数是否都正常,看 test_info.ini

#140 楼 @648981864 这个是取屏幕分辨率报错了,你是什么机型?

#136 楼 @121miao 你运行环境是什么?setup 失败,可以手动安装 setup 里的那几个依赖库

@joko 谢谢你的工具,你的 demo 可以运行,可是当我换了自己的包,报错:2017-02-16 20:49:24 [Error]:Failed to start appium :Message: An unknown server-side error occurred while processing the command. Original error: Error occured while starting App. Original error: Permission to start activity denied.

2017-02-16 20:49:24 [Error]:Try restarting the appium :5DYTG6DIVWEQT849,Trying the 1 frequency
是什么原因呢???

@joko 执行 Demo 报错,list index out of range 数组越界了。。

137楼 已删除

感謝,期待兼容 Python3 的版本~😃

厉害了,今天试了一下,很简易实用,特别是用例编写这块。作为伸手党,在此表示感谢了😙

134楼 已删除
133楼 已删除
132楼 已删除
131楼 已删除
130楼 已删除
129楼 已删除
匿名 #128 · 2017年01月12日

@joko 请教一个问题,你调试过 IOS 的多进程么?多台设备都是使用 WDA 的 8100 这一关端口么,还是用不同的端口?

在项目中实践了下,项目中或多或少都会用到 if esle 这样的逻辑,符合条件才执行某些分支,能把这个加上就更好了.
或者在用例中在加个一个标记 比如 test_xxx:True 如果为 True,这个步骤执行结果不影响下面其他步骤执行

这个很好,如果做成 excel 格式,然后一个固定格式,如点击,然后你需要选择 resID,在填写 ID 是多少,这样直接可以让测试小白用了。

#126 楼 @joko setup 与 tearDown 你那里写好了么?我试着写了一个自己方案,在用例文件夹里建立 setup.yaml tearDown.yaml
case_start() 时候过滤这两个文件

test_case_yaml_temp = public.GetCase.case_yaml_file()
test_case_yaml_temp.pop('setup.yaml')
test_case_yaml_temp.pop('tearDown.yaml')
test_case_yaml = test_case_yaml_temp.items()
然后在 get_all_case 再拼装 setup 和 tearDown 步骤,这样用一个流程下来也没问题,不知道还有没有更好的方案呢?

#122 楼 @potato 你好,只处理下多进程执行资源占用问题,查找 device,分配端口,剩下直接开启多进程执行代码即可。没有特别的处理

#124 楼 @yang_young 每个用例执行完毕后,会初始化环境(清除应用数据),不过如果服务端数据在上一用例有变更,目前框架层面没给解决,需要你独立出两套用例,或者你加个脚本,通过接口清理数据

#125 楼 @xiaocong168 明白你的意思。以用例层面实现单元测试内的 setup 与 tearDown,目前用例组织形式仅实现了 setup。按照目前框架已经实现的功能,你的用例应该这么写,other 继承 login,logout 继承 other。最近我会实现 tearDown 方法,到时候就能初始化话就和销毁环境了,不会向现在这么复杂。实现完我 @ 你

@joko 请教个问题
login.yaml --登陆用例

other.yaml --其他业务用例

logout.yaml -退出用例

以上三个用例在同一个文件夹下,其中 other.yaml 前面继承 login.yaml,后面继承 logout.yaml
运行时会执行 login.yaml 和 logout.yaml 一次,在 other.yaml 又会再再次运行

针对这个场景其实我主要目的是重点"其他业务用例",对于 login 和 logout 只是一个初始化和退出的过程,在执行 other.yaml 业务场景时运行一次即可。

这里是不是可以调整下,可以更灵活的组织用例呢?

如果前面的测试执行没有通过不会对后面的操作产生影响吗?

121楼 已删除

想问下你的多设备并行是怎么做的

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

学习了。

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

厉害!

#96 楼 @joko 楼主,我运行你的脚本,不知道为啥运行不起来,可以加你 QQ 吗?

#3 楼 @Jhon 这个功能试了很久都不知道怎么用

#116 楼 @joko Hi joko 谢谢你的提示! 感觉不是 unicodeIME 安装失败的原因,重新运行了下,从提示上看是说这个 apk 已存在, 我手动 -r 强行安装完成后,运行还是说 Failure [INSTALL_FAILED_ALREADY_EXISTS: 完成错误截图如下:

看到还有一个错误的信息:"message":"A new session could not be created. 不知道跟这个是不是有关系,我再查下看看。
另外我执行后,设备上没有出现需要确认的弹窗,只是安装了 unlock/appium settings/Auto Analysis,不知道得到这样的结果对不对,有点懵~😇

#112 楼 @xiaocong168 你好~感谢反馈。这块逻辑的确不严谨,我已经修改,晚些时候提交一下,谢谢!

#113 楼 @slideplustest 错误提示里写的是 unicodeIME 安装失败,这个是 appium 的输入法,执行的时候看下手机,点下按住确认

#114 楼 @xiaocong168 还是不行,appium-doctor 运行结果图如下,不知道怎么搞了😓

#113 楼 @slideplustest 运行 appium-doctor 检查下环境有没有问题

大神们,在 win7 系统执行报错,求教!
appium :1.4.16
python :2.7
device:nexus 6P
Android:7.1.1

HI @joko 感觉这里是不是有个逻辑问题
device_list = po.integration.get_device_info() 拼装当前历史已存在的 test_info and test_device 信息
capublic.GetDevice.set_device_yaml() 是获取当前设备信息并保存到 yaml

run_deviced 时候应该是当前最新的设备信息

不知道我理解对不对?

joko #109 · 2016年12月20日 Author

#110 楼 @JeffLiu 没用到 stf,用的是 stf 里 minicap 截图

@joko ,
设置这 minicap_path,minitouch_path,minicapso_path 有什么用么。是用到 STF 了么。

@joke 什么时候兼容 python 3,期待大神多发帖,多分享

#107 楼 @mexth
已经解决,使用命令行重新安装 appium 后可以正常跑了

目前遇到的问题是,运行 demo 脚本会自动打开 APPIUM 但是不会自动启动 server,然后也无法继续执行下一步。关闭 appium 就会报错。配置应该都 OK 的啊,APPIUM 版本 1.4.16 win10 64 位系统。appium 是手动安装的,环境变量也都设置了,就是图中的情况,貌似是侦测不到设备?APPIUM 也没有自动开启服务。

备注: 使用 RF+APPIUM 的方式是可以跑起来并运行一些 RF 上的 case 的。

#105 楼 @automation 感谢回复,linux 和 macos 做了处理,原因是因为 linux 和 macos 启动后会有独立的 logcat 进程,但 windows 不是(目前观察来看是这样),所以没办法单独关闭单独 logcat 的进程。windows 这部分模块没想好怎么处理

#104 楼 @joko 作为潜水党,首先感谢分享,发现一些问题,没有关闭 adb 和 cmd 进程,导致运行结束后还是不断写入 log 日志,自己运行后执行 taskkill /f /im cmd.exe

#102 楼 @zlx_lizzy 安装 app 失败了,检查下手机安装时是不是有一些弹窗,选择确认。目前仅封装了一些常见手机弹窗的处理

#103 楼 @xiaocong168 demo 自带了两个用例,第二个用例继承上一个用例(每次执行完毕后会初始化环境)

执行一次脚本 为什么会反复执行用例呢

你好,直接运行你的 demo_run 会报错,重装失败,类似这种:[Error]:Reinstalling com.wuba.zhuanzhuan P4M0215416001124
不知道和 appium 版本有没有关系,我的是 1.4.16...

#96 楼 @joko 好的,我整理一下 html 的代码,现在写的有点乱😅

请问一下,用例是随机执行的吗

厚着脸皮问一下前辈,没有选择 webdriver 中自带 get_screenshot_as_png() ,而使用 minicap 的考虑是哪里呢?

@joko 请问 python3.5 能用吗?

#92 楼 @gmjdadk 换 linux 把。html 改的很好看,你要有兴趣可以提交一下,我来合并。

#93 楼 @jaylin 1.用用例文件夹区分 。2:需要改动下遍历用例的方法,增加一些过滤关键字

#94 楼 @aya 1.这块代码我重写了,改成检测是否存在,你可以更新下。我也发现好像 7.0 获取不到信息。2.第二段报错的原因猜测是某些设备获取不到某个值,或者值的类型有问题。用 str 包裹一下就好。主分支我目前也这么改了,谢谢

install_info = self.adb.install_app(app_file_path).stdout.readlines();
print ("hehehehehehehhehe" + install_info[-1]);
if 'Success' in install_info[-1].strip():

请教楼主在之前的代码里,针对某些机型,上述代码会报错,但是把 stdout 改成 stderr 就不会错了,
还有一个问题就是,拉取了最新代码之后

return 'device_name:' + str(self.adb.get_device_name()), 'disk:' + str(self.adb.get_disk()), \
       'wifi_name:' + str(self.adb.wifi_name()), 'system_version:' + str(self.adb.get_android_version()), \
       'resolution:' + str(self.adb.get_screen_resolution())

这一段还是会报错,我就把没有加 str 转换的都加上了,就没有错了
@joko 多谢解答

请问不执行 testcase 中全部用例,执行其中几个或一个时,要怎么改

我这两天调试了一下,似乎是 pycharm 的 fork 问题,所有的 subprocess.Popen 都创建不了子进程,无奈放弃,只能用命令行运行了

稍微修改了下 html 的生成代码

在 win7 系统执行到下面就报错了。求教!

#85 楼 @gmjdadk 检查下 adb 环境配置,看看 pycharm 访问的 adb 是否与命令行相同

#88 楼 @a_little 与楼上相同,检查下 adb 配置,用 os.system 执行下 adb devices,看看设备链接状态

#86 楼 @NJ-zero 好的,我再看看,可能取设置属于的时候还是有问题

#88 楼 @a_little 换成命令行直接运行试试,我命令行一点问题都没有,换成 pycharm 各种问题,就算你写死一个设备,后面在启动 appium 的时候依然会报 nodejs 的错,我已经放弃了,就命令行吧

#84 楼 @gmjdadk 遇到同样的问题,一开始我以为是脚本不支持识别模拟器,后来换成真机 还是提示没有连接的设备

@joko 如果使用 id 定位找到的是一个元素组,想点击其中一个,testcase 应该怎么写呢?不考虑 xpath

匿名 #86 · 2016年12月09日

@gmjdadk @joko 下了最新的代码,还是报这个错误

#84 楼 @gmjdadk
然后我把 deviceID 直接写死尝试继续运行,又报了新的错误

真无奈了,命令行运行特顺利,换成 pycharm 一个问题接着一个问题

今天又遇到了另一个问题,我用命令行运行一点问题没有,换成 pycharm 运行,总是获取不到手机设备 [Error]:the computer is not connected to any devices 我打断点也看不出什么,好郁闷

@joko ,执行 python demo_run.py 这个, 是如何运行 Appium 的。运行 Appium 时候,是怎么调用 testCase 并输出报告的。Python 小白,没看明白,望楼主指点一二。

#81 楼 @joko 今天自己解决了,看了下你的代码和 doc 下的说明,之前的问题是没有修改 appium_parameter.yaml。😃

#78 楼 @gmjdadk 未来会支持,短期之内(二个月)不会支持

#79 楼 @cissy 帮忙贴一下安装那块的日志,我看下

#80 楼 @quqing

1:书写用例的方式已经极低了压缩撰写用力的的成本。

  • 例如原始用例:登录 - 发照片 - 注销
  • 需要变更为:登录 - 发作业 - 发照片 - 注销

在用例层只需变更用例的继承名即可。

2:cpu 与内存我觉得是报告里最有价值的东西,横向对比可分析出迭代后相同模块的性能异常问题,通过性能异常可深入追查代码层面异常,举个实际的例子:本次此用例业务内优化了加载的方案,从用户无法明显感知,所以就要从性能入手,确认优化效果

3.关于日志过滤只有 exception,大部分异常其实只能通过 debug 日志才看出,目前没有有效的方式处理,之后可能会增加关键词过滤

报告简洁明了,确实不错,我更想了解的是工具的实用性和内涵:

1.UI 频繁变动,如何解决维护成本问题;

2.性能图的 cpu 和 memory 能给前端性能分析带来哪些有用的价值;

3.日志能否记录下有助于开发定位问题的上下文(logcat 的错误关键字过滤,只用 exception 吗);

#76 楼 @joko app 是否安装成功的判断应该是有 bug,从手机上看是安装成功了,但 log 提示安装失败,用你自带的 app 是没有这个问题的,换成测试用的 app 就会出现。

#76 楼 @joko 请问高手,这套框架未来会支持 IOS 吗?

#76 楼 @joko 恩恩是的,我看了下是因为返回的设备信息没有类型,我都强转成 str 后,html 顺利的出来了

joko #76 · 2016年12月07日 Author

#72 楼 @JeffLiu 这个错误是我自定义的报错,原因是因为执行测试的手机,未安装上测试 app,尝试 4 次没成功后就会报错,你提供下你的电脑系统,和你的手机品牌与型号,我看看能不能找到 不能安装的问题。或者你尝试手动 adb install 试试

#74 楼 @gmjdadk 问题修复了,你拉下提交,应该是某些手机 wifi 状态问题导致

我照着错误提示,把 return 的参数类型强转成 str 就不报错了,html 出来了

复制的 log 不太好认,这是截图

我是 mac 环境,按照教程运行的很顺利,但是在最后生成报告的时候,没有 html,其他的截图什么的都有,唯独没有 html,请问是怎么回事呢?在 demo 运行的最后,报了这么堆异常:
Exception in thread Thread-1:
Traceback (most recent call last):
File "/usr/local/Cellar/python/2.7.11/Frameworks/Python.framework/Versions/2.7/lib/python2.7/threading.py", line 801, in bootstrap_inner
self.run()
File "/Users/shiwei/Desktop/Auto_Analysis/run.py", line 30, in run
a.case_start()
File "/Users/shiwei/Desktop/Auto_Analysis/po/integration.py", line 147, in case_start
self.analysis(yaml_name, yaml_path)
File "/Users/shiwei/Desktop/Auto_Analysis/po/integration.py", line 126, in analysis
return s.main()
File "/Users/shiwei/Desktop/Auto_Analysis/po/ExecuteCase.py", line 313, in main
self.
save_android_result()
File "/Users/shiwei/Desktop/Auto_Analysis/lib/Utils.py", line 125, in wrapper
t = func(*args, **kwargs)
File "/Users/shiwei/Desktop/Auto_Analysis/po/ExecuteCase.py", line 131, in save_android_result
r.main()
File "/Users/shiwei/Desktop/Auto_Analysis/public/GenerateReports.py", line 131, in main
self.
device_info(),
File "/Users/shiwei/Desktop/Auto_Analysis/lib/Utils.py", line 125, in wrapper
t = func(*args, **kwargs)
File "/Users/shiwei/Desktop/Auto_Analysis/public/GenerateReports.py", line 65, in __device_info
return 'device_name:' + self.adb.get_device_name(), 'disk:' + self.adb.get_disk(), \
TypeError: cannot concatenate 'str' and 'NoneType' objects

@joko ,
Run 程序会抛出这个异常。能简单介绍下运行原理么。

#70 楼 @joko 是 win10,今天早上拉了最新的代码,这个问题修复了,windows 上坑太多。

joko #70 · 2016年12月06日 Author

#69 楼 @cissy 你这是 win10 吗?你拉取最新的提交,我这边已经尝试修复了问题,win7 上测试没问题
#65 楼 @a_little


appium 启动确实有问题

joko #68 · 2016年12月06日 Author

#65 楼 @a_little
你方便留下联系方式吗,我远程看看异常状态,刚才楼下的朋友问题是因为 appium 配置问题,我远程连接看了下,异常已经解决了

joko #67 · 2016年12月06日 Author

#66 楼 @JeffLiu
#65 楼 @a_little

稍等,我正在看这个问题,今天应该能解决,原因是 windows 启动进程的方法有问题,linux 和 macos 我测了没问题

@joko ,我和 61# 一样的错。已安装你的最新版了。日志也没记录啥呀。Appium server 还是不能启动。

#64 楼 @joko 刚刚在 git 新下载的源码,终端 mx5pro,执行 demo_run,安装好 apk 后就不能继续了,请问这个报错是什么原因呢,日志一直是:
[34m2016-12-06 15:36:20 [Debug]:[0m

joko #64 · 2016年12月06日 Author

#63 楼 @xiaocong168 不需要手动打开,代码里已经用命令方式开启,我在最新的提交里加了启动的日志,你再失败的话,把日志贴出来看看

#62 楼 @joko 我安装的是通过下载的 window 客户端版本的 appium 运行 demo_run 时候 需要手动打开 appium 吗?

joko #62 · 2016年12月06日 Author

#61 楼 @xiaocong168
#57 楼 @JeffLiu

这个问题都是因为 appium 没起来,你们要是方便的话重新拉取下 github 最新的提交,我在这块加了写 log,用于分析问题

安装测试的 apk 后就一直不动了

#57 楼 @JeffLiu 不用手动开启 appium 的,你的电脑是什么系统,win10?

#58 楼 @a_little {}是 Python .format() 方法用于拼接字符串,不相同手机所需的 minicap 包均不相同,所以查到手机信息后用这个来替换路径

#59 楼 @xiaocong168 你这个报错是因为手机版本是 4.1.2,获取不到手机的 resource-id,需要手 4.2.2 以上的手机

安装后后报错,为什么呢

@joko 你好,请问这个路径中的{ }大括号用意是什么呀,百度了半天无解,只好来咨询你,看起来是多选的意思,在这个路径下的多个文件,是这样的意思吗?minicap_path = /Users/joko/Documents/Auto_Analysis/data/minicap/bin/{}/minicap

怎么一直停在这里不运行了呢。Appium Server 要开启么。

好好学习

想请教大家一个问题,appium 在做安卓 app 自动化测试时,点击 h5 页面 js 弹框按钮,执行的时候没有效果也不报错,为什么呢
代码是这样写的:driver.findElementByXPath("//android.view.View[contains(@content-desc,'返回修改')]").click();
通过 inspector 获取的元素内容如下

就连点击坐标也没有用

厉害了

#52 楼 @joko 太强大了,看你的代码才是真正的高手写的,够我消化好长时间了。。。。

#48 楼 @410637312
#50 楼 @lose
设计框架的时候需要考虑到这些,用的是单例模式,每个线程不会互相影响。关于错误的捕捉:

1.来看代码,解释器方法包裹了一个错误捕捉器。用来捕获执行成错误的原因


def e():
    """
    捕获用例执行函数异常安装
    :return: True|AssertionError|AttributeError
    """

    def E(func):
        def wrapper(*args, **kwargs):
            error_msg = True
            try:
                return func(*args, **kwargs)
            except AssertionError as e:
                U.Logging.warn(traceback.format_exc())
                U.Logging.error(e)
                error_msg = 'Assertion error'
            except AttributeError as e:
                U.Logging.warn(traceback.format_exc())
                U.Logging.error(e)
                error_msg = 'Attribute Error'
            except Exception as e:
                error_msg = traceback.format_exc()
                U.Logging.error(e)
            finally:
                return error_msg

        return wrapper

    return E

    @e()
    def __analysis_yaml(self, path_yaml):
      pass

2.错误日志的捕捉

  • 1:应用启动时在后台记录 log
  • 2:任务结束时分析 log,过滤 exception

    def logcat(self, log_path):
        return self.adb('logcat -v time > %s&' % (log_path))

class Anl:
    def __init__(self, all_result_path):
        self.all_result_path = all_result_path

    @U.l()
    def __log_file(self, all_path_result, the_suffix_name):
        """

        :return: 日志列表
        """
        return GetFilePath.all_file_path(
            all_path_result, the_suffix_name).values()

    def analyze(self, log_file):
        """
        过滤Exception到log文件夹内
        :param log_file: log的路径
        :return:
        """
        errorId = 0
        go_on_id = 0
        log_filter_name = os.path.split(log_file)[1].split('.')[0]
        with open(self.all_result_path + '/log/{}filter.log'.format(log_filter_name), 'w') as s:

            with open(log_file) as f:
                for line in f:
                    if 'Exception' in line:
                        go_on_id = 1
                        s.write('#' + '-' * 40 + '\n')
                        s.write(line)
                        errorId = line.split('(')[1].split(')')[0].strip()
                    elif go_on_id == 1:
                        if errorId in line:
                            s.write(line)
                        else:
                            go_on_id = 0

    def main(self):
        """
        获取log,生成filter log
        :return:
        """
        for log_file in self.__log_file(self.all_result_path, '.log'):
            self.analyze(log_file)


这样执行过程中的异常和错误的异常均能捕捉,查看报告即可

#24 楼 @seveniruby 提个问题 :本篇标题太长 首页看不出是精华 钻石没了

@joko 我也想知道你的异常怎么捕捉的,比如三台手机,有两台手机闪退了,如何捕捉到这两台闪退手机的日志信息并且在不影响到框架的正常运行的?

先顶后看。😃

我想问下你的 “执行过程中会被异常打断,目前已经捕获这些异常,增加了判断” 这个是怎么实现的

joko #47 · 2016年12月01日 Author

#43 楼 @Jhon
#41 楼 @liruiyu
#35 楼 @sunkuan2007
如果你们都是 python2.7 的话,重新 git 下我最新的提交,我换了一种多进程的启动方法。看看还会不会报错

#45 楼 @Tank007 你好
问题 1:
可以直接显示在 html 上,启动测试的时候同时启动个线程读 logcat,发现关键字,就把那段日志存到 html 即可,目前没做的原因是因为我目前还不会 js,做不出那种开关按钮的效果。不然错误日志都展示的话,样式太丑

问题 2:目前你看到的是每个测试用例是单独的日志,linux 上和 macos 处理的都比较完美,windows 目前还没找到完美的解决办法

匿名 #45 · 2016年12月01日

#44 楼 @joko 请教一个问题,测试报告你的报错是通过链接来查看,如果我想把报错日志信息直接显示到 html 中,这个有没有什么好方法,跑完通过过滤日志这种方法不是很好,能有其他的方法,比如类似监听什么的,可以把对应用例的报错或者执行日志单独过滤出来么?类似 testng 那样

joko #44 · 2016年12月01日 Author

#42 楼 @Jhon
#41 楼 @liruiyu 你们的 python 环境是多少?电脑环境是什么?

#38 楼 @a_little 没呢,我和 41 楼,一样的错误。。。。。。。

#41 楼 @liruiyu
#41 楼 @liruiyu 我和你一样的错误呀。。。。


这个报错跟我安装的 appium 版本有关系么。 我的版本是 1.4.16 !!!请大神指教,!!在线等

@joko 好呢,多谢分享呀,百度了这个错是 python2 和 3 的编码不同引起的,试着解决还没果

joko #39 · 2016年11月30日 Author

#38 楼 @a_little python3 我还没开始兼容呢,所以会报错,近期会兼容 python3

@Jhon hello,你用 python3 跑通了吗,我调试了下 现在这列报错,@joko请问有解决方案吗?

37楼 已删除

求指教,没有接触过自动化测试工具,如何入门 appium,网上很多资料都是互相转载的甚至都没经过亲身验证,实际帮助不大,想请教有经验的人指点一下,如何制定学习策略

#10 楼 @JonnyNan AttributeError: 'module' object has no attribute 'case_sun' 我也报这个错误,单独运行 integration.py 没有报错。你的这个报错解决了吗?

#33 楼 @jphtmt yaml 并不是用 ConfigParser 实现的,这两个不能混为一谈。
在本工具内:ini 作为信息配置管理与路径管理,yaml 作为 testcase 的管理。

这个的区别是:ini 有更好的索引,天生适合作为配置文件。yaml 的优势在于支持多种数据格式,适合与语言直接粘合

#31 楼 @joko 还有,你的 yaml 是用 ConfigParser 实现的,为啥不直接用 ini 格式的配置文件呢

#10 楼 @JonnyNan 请问这个问题你怎么解决的啊,我也是报这个错误

#29 楼 @jphtmt 感谢提醒。这块暂时问题不大,因为随机到的可能较小,且假设端口已经被占用,会重新分配端口。

支持分享

这段代码,直接随机生成端口,感觉会有冲突的问题啊,是不是应该增加一个端口检测是否占用的方法
@joko

写的挺好的👍

@joko 好的。我也是用的 python3,没想到是这个的原因,嘿嘿。感谢你的分享,我觉得很有创意。

#25 楼 @joko 好的,下个 pycharm 用下,问题我再自己看看。thanks

#18 楼 @a_little 目前不兼容 python3,不过从报错原因来看是因为 configparser 没获取到 minicap 的绝对路径,你可以直接手动配置 data/test.ini 文件。配置说明你可以看 doc 内的 parameter_configuration.md。把里面所有的字段都给手动配置了。
#21 楼 @Jhon 没访问到包,你也手动配置 data/test.ini 的参数信息后,然后运行 po.integration.py。这是单线程的方法,可以获取到更详细的报错信息

我在 windows,linux,macos 上都运行测试过,所以大多错误应该是环境的问题,可以看项目里写的详细文档

建议 Python 新手首次运行使用 pycharm,用强大的 ide 如同老师可以帮你处理很多事情。

加精理由: 写的详细. 设计优秀. 期待以后继续改进造福大家.

思寒_seveniruby 将本帖设为了精华贴 11月29日 14:27
22楼 已删除

#12 楼 @joko @JonnyNan return recv()
AttributeError: 'module' object has no attribute 'case_sun'

报这个错是什么原因呀。。。。

#10 楼 @JonnyNan 我试试看。

#12 楼 @joko 昨天在忙其他事情,我现在试试,社区这么多热心人回复,好开心。。。。

@joko 我用的 python3.4,调试到这步了,报这个错,还需要设置什么呢?configparser.NoSectionError: No section: 'minicap'

为什么我就不能造出如此好轮子?不开心。

匿名 #16 · 2016年11月29日

#5 楼 @seveniruby 这个为什么没有设置为精华帖啊,目录结构比较清晰,封装也很不错,值得学习。嘿嘿,强烈建议设置为精华帖,让更多的人可以学习到。支持@joko

15楼 已删除
14楼 已删除
13楼 已删除
joko #12 · 2016年11月28日 Author

#10 楼 @JonnyNan
#8 楼 @Jhon 问题解决了吗?手机是需要开启 usb 调试的,不然电脑和手机无法通信

@Jhon 我跟你图片一样的结果,原因是我没开启 usb 调试工具,在手机中的 开发人员选项里。开启后 还要到门口允许这个电脑 usb 调试,就不报这个错了。


图片在此

9楼 已删除

#5 楼 @seveniruby python demo_run.py 运行这步骤后报了这个错,是什么原因呀。?

这个厉害,测试报告也很漂亮

@seveniruby 我没安装 pip。我安装试试。谢谢你的解答思寒

之前的 appiumbooster 也是这样实现的 你们可以交流下

joko #4 · 2016年11月28日 Author

#3 楼 @Jhon 你 python 环境配置 pip 了吗?,setup 的原理是调用 pip 安装。如果配置了还是报错。尝试手动安装库:
'pyyaml','matplotlib','Appium-Python-Client','selenium','termcolor'

pip install 库名

安装成功后,即可运行,python demo_run.py

@joko python setup.py install 执行这个步骤失败了,怎么回事呀。

匿名 #2 · 2016年11月28日

@lose 跟你框架功能差不多,综合学习一下

谢谢分享

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

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