Appium PageObject+Python+Appium 自动化测试 (支持 Android & iOS)

测试大头兵 · 2018年08月27日 · 最后由 huangwang 回复于 2022年06月14日 · 3100 次阅读

前人栽树,后人乘凉,在 @lose (测试小书童) 源码的基础上增加适配 iOS 设备。第二次在@lose (测试小书童) 源码的基础上修改脚本了,感谢前人付出的努力~

@lose (测试小书童) 源码地址:https://testerhome.com/topics/8939

0918 UPDATE

1.新增从某控件开始滑动操作(上下左右)方法
2.新增点击屏幕/重复操作某一元素/忽略某控件等方法

0910 UPDATE

1.新增控件集参数化,相同测试步骤的 Android/iOS 可共用一份测试用例
2.不同测试步骤的用例还需要单独写

0904 UPDATE

1.优化 Android log 及 crsahinfo 相关输出路径
2.新增 iOS crashreport 解析

新增内容:

1.适配 iOS
2.提取 android crash 信息
3.优化 report(增加自动填充包名,app 名称,版本,bundleId 等信息)

简介

采用 python3+appium1.8,基于 PageObject 框架的 UI 自动化测试持续集成。

  • unittest 参数化
  • PageObject 分层管理
  • 用例编写基于 yaml 配置多关键字驱动
  • 自动生成 excel 测试报告
  • 同时支持 Android/iOS
  • 支持多设备执行
  • 支持 Windows/Mac OS(iOS 必须使用 Mac OS)

目录结构

1.app

待测apk/ipa 安装包路径
uiautomator2等安装包路径

2.Base

Android 测试相关:
BaseAdb.py
BaseAndroidPhone.py
BaseApk.py
BaseLog.py
BaseLogcat.py

iOS 测试相关:
BaseIosPhone.py
BaseIpa.py
BaseIosLog.py

数据处理相关:
BaseConfig.py
BaseExcel.py
BaseFile.py
BasePickle.py
BaseYaml.py
BaseOperate.py
BaseReplace.py

测试执行相关:
BaseAppiumServer.py
BaseInit.py
BaseRunner.py
BaseElements.py

报告相关:
BaseStatisics.py
BaseError.py
BaseEmail.py

3. iOSCrashAnalysis

iOS crash report  解析相关:
BaseIosCrash.py 解析脚本
FileOperate.py 文件操作相关
symbolicatecrash  xCode自带的解析工具,获取方式:find /Applications/Xcode.app -name symbolicatecrash -type f,复制过来就行了

4.Log

设备日志及持久化数据
操作日志,失败截图
crash解析结果

5.PageObject

操作的封装及测试结果统计
测试用例模块分级

6.其他

../Report       =====测试报告
../Runner       =====执行文件
../TestCase     =====测试用例集
../yamls        =====用例管理

主要功能

1.基础测试类及方法

  • 获取 apk/ipa 安装包信息
  • 获取 Android/iOS 设备信息
  • 自动分配端口并启动 appiumserver
  • 设备日志及 crashlog 分析
  • 失败重试
  • 失败截图
  • 报告统计及输出
  • 邮件发送
  • case 管理
  • 常用操作封装
  • 其他

2.yaml 编写说明

testinfo: 表示用例介绍
    - id: 用例id
    - title: 用例标题
    - info: 前置条件
testcase: 用例的执行步骤
    - element_info: //XCUIElementTypeStaticText[@name="剪辑"] 元素
    -  find_type: id  元素类型
        - id
        - xpath
        - name
        - text
        - ids 需要增加index
        - index 和ids/xpaths/texts等配合 
        - class_name
        - ios_id
        - predicate

    - operate_type: click 操作
        - click
        - swipe_down
        - swipe_up
        - get_value
        - set_value
        - screen_tap
        - swipe_left
        - swipe_right
        - msg 传给set_value关键字
        - adb_tab 使用adb中的tab命令点击元素,元素必须可识别,应用于悬浮层场景
        -  get_content_desc 无法切换到webview时,用此关键字
        - press_key_code 键盘触发事件,需要传code
        - code 传给press_key_code关键字
        - is_webview:1 为1表示切换到webview,为2表示切换到原生
        - 其他关键字 用于定制一些特殊业务
    - is_time: 3 自定义暂停3秒
    - info: 点击动态列表第一条数据 操作步骤介绍

- check: 检查点,支持多检查点
  - element_info: //XCUIElementTypeStaticText[@name="剪辑"]
  - find_type: ids
  - index: 0
  - operate_type:
    - contrary"  相反检查点,表示如果检查元素存在就说明失败,如删除后,此元素依然存在
    - contrary_getval  检查点关键字contrary_getval: 相反值检查点,如果对比成功,说明失败
    - default_check  默认检查点,就是查找页面元素
    - compare 历史数据和实际数据对比
    - toast  toast检查
  - info: 查找是否存在历史记录

3.yaml 实例


==========================================================
testinfo:
    - id: home_test_001
      title: 启动app并进入gallery
      info: 打开app并点击高级编辑
testcase:
    - element_info: camerta_n
      find_type: ios_id
      operate_type: click 
      info: 点击创作中心主按钮

    - element_info: //XCUIElementTypeStaticText[@name="剪辑"]
      find_type: xpath
      operate_type: click
      info: 点击剪辑按钮

    - element_info: 跳过
      find_type: name
      operate_type: click
      info: 跳过升级页面

    - element_info: //XCUIElementTypeStaticText[@name="剪辑"]
      find_type: xpath
      operate_type: click
      info: 点击剪辑按钮

    - element_info: 好
      find_type: name
      operate_type: click
      info: 授权存储

    - element_info: 好
      find_type: name
      operate_type: click
      info: 授权相册

check:
    - element_info: //XCUIElementTypeButton[@name="下一步"]
      find_type: xpath
      check: default_check
      info: 进入'Gallery'页面成功

4.某个用例的 page 层

from PageObject import Pages

class PageOperate:
    def __init__(self, kwargs):
        _init = {"driver": kwargs["driver"], "test_msg": getYam(kwargs["path"]), "device": kwargs["device"],
                 "logTest": kwargs["logTest"], "platformName": kwargs["platformName"],"caseName": kwargs["caseName"]}
        self.page = Pages.PagesObjects(_init)

    def operate(self):  # 操作步骤
        self.page.operate()

    def checkPoint(self):  # 检查点
        self.page.checkPoint()

5.testcase 层调用 page 层

tc_temp = PATH("../yamls/temp.yaml")
el_android = PATH("../yamls/el_android.yaml")
el_iOS = PATH("../yamls/el_iOS.yaml")

class HomeTest(ParametrizedTestCase):

    def repalce(self, tc, tc_temp):#用了最笨的替换字符串方法,输出一个临时temp.yaml文件,测试完成后再删除
        if self.platformName == 'android':
            ReplaceYaml(tc, tc_temp, el_android)
        elif self.platformName == 'iOS':
            ReplaceYaml(tc, tc_temp, el_iOS)

    def testFirstOpen(self):
        tc = PATH("../yamls/home/firstOpen.yaml")
        self.repalce(tc, tc_temp)
        app = {"logTest": self.logTest, "driver": self.driver, "path": tc_temp,
               "device": self.udid, "platformName": self.platformName, "caseName": sys._getframe().f_code.co_name}

        page = PageOperate(app)
        page.operate()
        page.checkPoint()

    def testSecondOpen(self):
        tc = PATH("../yamls/home/secondOpen.yaml")
        self.repalce(tc, tc_temp)

        app = {"logTest": self.logTest, "driver": self.driver, "path": tc_temp,
               "device": self.udid, "platformName": self.platformName, "caseName": sys._getframe().f_code.co_name}

        page = PageOperate(app)
        page.operate()
        page.checkPoint()

6.Case 入口

def runnerCaseApp(devices):
    starttime = datetime.now()
    suite = unittest.TestSuite()
    suite.addTest(ParametrizedTestCase.parametrize(HomeTest, param=devices))
    # suite.addTest(ParametrizedTestCase.parametrize(HomeTest, param=devices)) #加入测试类
    unittest.TextTestRunner(verbosity=2).run(suite)
    endtime = datetime.now()
    countDate(datetime.now().strftime('%Y-%m-%d %H:%M:%S'), str((endtime - starttime).seconds) + "秒")

7.实时日志展示

testFirstOpen (TestCase.HomeTest.HomeTest) ... ==操作步骤:com.quvideo.xiaoying:id/xiaoying_alert_dialog_positive_click  ==
==操作步骤:com.android.packageinstaller:id/permission_allow_button_click  ==
==操作步骤:com.android.packageinstaller:id/permission_allow_button_click  ==
==操作步骤:com.quvideo.xiaoying:id/wel_skip_click  ==
==操作步骤:com.quvideo.xiaoying:id/layout_fragment_creation_click  ==
==操作步骤:com.quvideo.xiaoying:id/icon1_click  ==
==操作步骤:text("跳过")_click  ==
==操作步骤:com.quvideo.xiaoying:id/icon1_click  ==
==操作步骤:text("其他相册")_   ==
Platform: android
Device: 4ed397ac
==用例_启动app并进入gallery检查点成功==
ok

8.操作日志输出展示

2018-08-23 11:51:08,390  - INFO - ----  home_test_001_启动app并进入gallery_com.quvideo.xiaoying:id/xiaoying_alert_dialog_positive_click          ----
2018-08-23 11:51:09,711  - INFO - ----  home_test_001_启动app并进入gallery_com.android.packageinstaller:id/permission_allow_button_click          ----
2018-08-23 11:51:10,583  - INFO - ----  home_test_001_启动app并进入gallery_com.android.packageinstaller:id/permission_allow_button_click          ----
2018-08-23 11:51:19,866  - INFO - ----  home_test_001_启动app并进入gallery_com.quvideo.xiaoying:id/wel_skip_click          ----
2018-08-23 11:51:23,644  - INFO - ----  home_test_001_启动app并进入gallery_com.quvideo.xiaoying:id/layout_fragment_creation_click          ----
2018-08-23 11:51:29,023  - INFO - ----  home_test_001_启动app并进入gallery_com.quvideo.xiaoying:id/icon1_click          ----
2018-08-23 11:51:30,361  - INFO - ----  home_test_001_启动app并进入gallery_text("跳过")_click          ----
2018-08-23 11:51:33,660  - INFO - ----  home_test_001_启动app并进入gallery_com.quvideo.xiaoying:id/icon1_click          ----
2018-08-23 11:51:35,636  - INFO - ----  home_test_001_启动app并进入gallery_text("其他相册")_           ----
2018-08-23 11:51:35,698  - INFO - [CheckPoint_1]: testFirstOpen_ : OK
2018-08-23 11:51:52,341  - INFO - ----  home_test_002_进入拍摄页面_com.quvideo.xiaoying:id/img_creation_click          ----
2018-08-23 11:51:54,148  - INFO - ----  home_test_002_进入拍摄页面_com.quvideo.xiaoying:id/icon2_click          ----
2018-08-23 11:51:55,116  - INFO - ----  home_test_002_进入拍摄页面_text("允许")_click          ----
2018-08-23 11:51:56,704  - INFO - ----  home_test_002_进入拍摄页面_text("总是允许")_click          ----
2018-08-23 11:51:57,834  - INFO - ----  home_test_002_进入拍摄页面_text("总是允许")_click          ----
2018-08-23 11:52:01,559  - INFO - ----  home_test_002_进入拍摄页面_text("高清相机")_           ----
2018-08-23 11:52:01,647  - INFO - [CheckPoint_2]: testSecondOpen_ : OK

9.crash 解析-android


=========================crash================================
06-20 13:41:06.165  7638  7638 E AndroidRuntime: Process: com.quvideo.xiaoying, PID: 7638
06-20 13:41:06.165  7638  7638 E AndroidRuntime: java.lang.NullPointerException: Attempt to read from field 'int com.quvideo.xiaoying.datacenter.social.publish.PublishTaskInfo.step' on a null object reference
06-20 13:41:06.165  7638  7638 E AndroidRuntime:  at com.quvideo.xiaoying.app.publish.d.a.aI(SourceFile:67)
06-20 13:41:06.165  7638  7638 E AndroidRuntime:  at com.quvideo.xiaoying.app.publish.d.a.aK(SourceFile:123)
06-20 13:41:06.165  7638  7638 E AndroidRuntime:  at com.quvideo.xiaoying.app.publish.d.a.a(SourceFile:151)
06-20 13:41:06.165  7638  7638 E AndroidRuntime:  at com.quvideo.xiaoying.app.publish.SocialPublishBaseActivity.da(SourceFile:1531)
06-20 13:41:06.165  7638  7638 E AndroidRuntime:  at com.quvideo.xiaoying.app.publish.SocialPublishBaseActivity.aaM(SourceFile:1565)
06-20 13:41:06.165  7638  7638 E AndroidRuntime:  at com.quvideo.xiaoying.app.publish.c.b$1.acg(SourceFile:312)
06-20 13:41:06.165  7638  7638 E AndroidRuntime:  at com.quvideo.xiaoying.videoeditor.j.a.a$4.m(SourceFile:650)
06-20 13:41:06.165  7638  7638 E AndroidRuntime:  at com.quvideo.xiaoying.ui.dialog.c.onClick(SourceFile:165)
06-20 13:41:06.165  7638  7638 E AndroidRuntime:  at android.view.View.performClick(View.java:6291)
06-20 13:41:06.165  7638  7638 E AndroidRuntime:  at android.view.View$PerformClick.run(View.java:24931)
06-20 13:41:06.165  7638  7638 E AndroidRuntime:  at android.os.Handler.handleCallback(Handler.java:808)
06-20 13:41:06.165  7638  7638 E AndroidRuntime:  at android.os.Handler.dispatchMessage(Handler.java:101)
06-20 13:41:06.165  7638  7638 E AndroidRuntime:  at android.os.Looper.loop(Looper.java:166)
06-20 13:41:06.165  7638  7638 E AndroidRuntime:  at android.app.ActivityThread.main(ActivityThread.java:7425)
06-20 13:41:06.165  7638  7638 E AndroidRuntime:  at java.lang.reflect.Method.invoke(Native Method)
06-20 13:41:06.165  7638  7638 E AndroidRuntime:  at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:245)
06-20 13:41:06.165  7638  7638 E AndroidRuntime:  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:921)
06-20 13:41:06.367   732   732 E wificond: Failed to get NL80211_RATE_INFO_NOISE
06-20 13:41:06.367   732   732 E wificond: Failed to get NL80211_RATE_INFO_SNR
06-20 13:41:06.367   732   732 E wificond: Failed to get NL80211_STA_INFO_CNAHLOAD
06-20 13:41:07.841  1157  1174 I chatty  : uid=1000(system) android.ui expire 3 lines
06-20 13:41:07.851  1448  1780 I HwNetworkPolicyManager: getHwUidPolicy uid = 10063 policy = 0
06-20 13:41:07.878  1157  2823 I chatty  : uid=1000(system) Binder:1157_F expire 1 line
06-20 13:41:07.879  1157  1350 I chatty  : uid=1000(system) ConnectivitySer expire 14 lines
06-20 13:41:07.879  1157  8282 I chatty  : uid=1000(system) Binder:1157_1B expire 10 lines
06-20 13:41:07.893  1157  8281 I chatty  : uid=1000(system) Binder:1157_1A expire 12 lines
06-20 13:41:07.900 17940 17940 I ActivityThread: Removing dead content provider:android.content.ContentProviderProxy@b3b0cdb
06-20 13:41:07.911  1157 14597 I chatty  : uid=1000(system) Binder:1157_1F expire 9 lines
06-20 13:41:07.936  1157  1167 I chatty  : uid=1000(system) Binder:1157_1 expire 14 lines
=========================crash=========================
06-20 13:41:06.165  7638  7638 E AndroidRuntime: Process: com.quvideo.xiaoying, PID: 7638
06-20 13:41:06.165  7638  7638 E AndroidRuntime: java.lang.NullPointerException: Attempt to read from field 'int com.quvideo.xiaoying.datacenter.social.publish.PublishTaskInfo.step' on a null object reference
06-20 13:41:06.165  7638  7638 E AndroidRuntime:  at com.quvideo.xiaoying.app.publish.d.a.aI(SourceFile:67)
06-20 13:41:06.165  7638  7638 E AndroidRuntime:  at com.quvideo.xiaoying.app.publish.d.a.aK(SourceFile:123)
06-20 13:41:06.165  7638  7638 E AndroidRuntime:  at com.quvideo.xiaoying.app.publish.d.a.a(SourceFile:151)
06-20 13:41:06.165  7638  7638 E AndroidRuntime:  at com.quvideo.xiaoying.app.publish.SocialPublishBaseActivity.da(SourceFile:1531)
06-20 13:41:06.165  7638  7638 E AndroidRuntime:  at com.quvideo.xiaoying.app.publish.SocialPublishBaseActivity.aaM(SourceFile:1565)
06-20 13:41:06.165  7638  7638 E AndroidRuntime:  at com.quvideo.xiaoying.app.publish.c.b$1.acg(SourceFile:312)
06-20 13:41:06.165  7638  7638 E AndroidRuntime:  at com.quvideo.xiaoying.videoeditor.j.a.a$4.m(SourceFile:650)
06-20 13:41:06.165  7638  7638 E AndroidRuntime:  at com.quvideo.xiaoying.ui.dialog.c.onClick(SourceFile:165)
06-20 13:41:06.165  7638  7638 E AndroidRuntime:  at android.view.View.performClick(View.java:6291)
06-20 13:41:06.165  7638  7638 E AndroidRuntime:  at android.view.View$PerformClick.run(View.java:24931)
06-20 13:41:06.165  7638  7638 E AndroidRuntime:  at android.os.Handler.handleCallback(Handler.java:808)
06-20 13:41:06.165  7638  7638 E AndroidRuntime:  at android.os.Handler.dispatchMessage(Handler.java:101)
06-20 13:41:06.165  7638  7638 E AndroidRuntime:  at android.os.Looper.loop(Looper.java:166)
06-20 13:41:06.165  7638  7638 E AndroidRuntime:  at android.app.ActivityThread.main(ActivityThread.java:7425)
06-20 13:41:06.165  7638  7638 E AndroidRuntime:  at java.lang.reflect.Method.invoke(Native Method)
06-20 13:41:06.165  7638  7638 E AndroidRuntime:  at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:245)
06-20 13:41:06.165  7638  7638 E AndroidRuntime:  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:921)
06-20 13:41:06.367   732   732 E wificond: Failed to get NL80211_RATE_INFO_NOISE
06-20 13:41:06.367   732   732 E wificond: Failed to get NL80211_RATE_INFO_SNR
06-20 13:41:06.367   732   732 E wificond: Failed to get NL80211_STA_INFO_CNAHLOAD
06-20 13:41:07.841  1157  1174 I chatty  : uid=1000(system) android.ui expire 3 lines
06-20 13:41:07.851  1448  1780 I HwNetworkPolicyManager: getHwUidPolicy uid = 10063 policy = 0
06-20 13:41:07.878  1157  2823 I chatty  : uid=1000(system) Binder:1157_F expire 1 line
06-20 13:41:07.879  1157  1350 I chatty  : uid=1000(system) ConnectivitySer expire 14 lines
06-20 13:41:07.879  1157  8282 I chatty  : uid=1000(system) Binder:1157_1B expire 10 lines
06-20 13:41:07.893  1157  8281 I chatty  : uid=1000(system) Binder:1157_1A expire 12 lines
06-20 13:41:07.900 17940 17940 I ActivityThread: Removing dead content provider:android.content.ContentProviderProxy@b3b0cdb
06-20 13:41:07.911  1157 14597 I chatty  : uid=1000(system) Binder:1157_1F expire 9 lines
06-20 13:41:07.936  1157  1167 I chatty  : uid=1000(system) Binder:1157_1 expire 14 lines
06-20 13:41:07.942  1157  1367 I chatty  : uid=1000(system) CallbackHandler expire 2 lines
06-20 13:41:07.958  1157  1182 I chatty  : uid=1000(system) android.display expire 1 line

10. crash 解析-iOS


============开始导出crashreport==========
idevicecrashreport -u 5214866ccb9342f87f4c2aab093c25f7e252fd85 /Users/zhulixin/Desktop/python-appium/Log/CrashInfo/iOS/Before/
Move: WiFi/WiFiManager/wifi-buf-05-23-2018__18:35:55.107.log
Move: WiFi/WiFiManager/wifi-buf-08-12-2018__02:40:07.213.log
Move: WiFi/WiFiManager/wifi-buf-05-06-2018__21:15:54.957.log
Move: WiFi/WiFiManager/wifi-buf-06-19-2018__05:16:04.564.log
Move: WiFi/WiFiManager/wifi-buf-06-16-2018__10:05:31.097.log
Move: WiFi/WiFiManager/wifi-buf-11-04-2017__14:36:48.log
Move: WiFi/WiFiManager/wifi-buf-12-03-2017__14:31:53.log
Move: WiFi/WiFiManager/wifi-buf-11-14-2017__22:41:35.log
Move: WiFi/WiFiManager/wifi-buf-05-13-2018__23:07:34.084.log
Move: WiFi/WiFiManager/wifi-buf-06-19-2018__08:05:54.196.log
Move: WiFi/WiFiManager/wifi-buf-01-23-2018__23:50:53.018.log
Move: WiFi/WiFiManager/wifi-buf-12-03-2017__15:54:45.log
Move: WiFi/WiFiManager/wifi-buf-04-10-2018__14:33:15.105.log
Move: WiFi/WiFiManager/wifi-buf-08-12-2018__20:34:03.165.log
Move: WiFi/WiFiManager/wifi-buf-08-24-2018__02:50:49.140.log
.....
.....

Move: XiaoYing-2018-07-30-114612.ips
Move: XiaoYing-2018-07-30-162434.ips
Move: XiaoYing-2018-07-28-123234.ips
Move: XiaoYing-2018-09-04-102545.ips
Move: XiaoYing-2018-07-31-095526.ips
Move: XiaoYing-2018-07-31-151350.ips
Move: XiaoYing-2018-09-04-102545.ips
Move: XiaoYing-2018-07-30-113126.ips
Move: XiaoYing-2018-07-30-114612.ips
Move: com.apple.appstored/appstored.log
Done.
============开始解析待测app相关crashreport==========
输入的文件为: /Users/zhulixin/Desktop/python-appium/Log/CrashInfo/iOS/Before/XiaoYing-2018-07-30-114612.ips
输出的文件为: 
0x100c24000 - 0x102ff3fff XiaoYing arm64  <2deaa9887c173bb0a9f4b051e47f04a3> /var/containers/Bundle/Application/170BDC50-F0D3-4973-9781-D414532E21CD/XiaoYing.app/XiaoYing
2DEAA988-7C17-3BB0-A9F4-B051E47F04A3
'/dSYMs/XiaoYing.app.dSYM'

输入的文件为: /Users/zhulixin/Desktop/python-appium/Log/CrashInfo/iOS/Before/XiaoYing-2018-07-30-162434.ips
输出的文件为: 
0x100290000 - 0x10265ffff XiaoYing arm64  <2deaa9887c173bb0a9f4b051e47f04a3> /var/containers/Bundle/Application/C5855F55-13D0-49FE-ADC2-7C82565237D0/XiaoYing.app/XiaoYing
2DEAA988-7C17-3BB0-A9F4-B051E47F04A3
'/dSYMs/XiaoYing.app.dSYM'

......
......
============crashreport解析完成==========

============删除所有解析之前的crash文件==========
/Users/zhulixin/Desktop/python-appium/Log/CrashInfo/iOS/Before/JetsamEvent-2018-09-04-104135.ips was removed!
Directory: /Users/zhulixin/Desktop/python-appium/Log/CrashInfo/iOS/Before/JetsamEvent-2018-09-04-104135.ips was removed!
Directory: /Users/zhulixin/Desktop/python-appium/Log/CrashInfo/iOS/Before/WiFi was removed!
/Users/zhulixin/Desktop/python-appium/Log/CrashInfo/iOS/Before/XiaoYing-2018-07-30-114612.ips was removed!
Directory: /Users/zhulixin/Desktop/python-appium/Log/CrashInfo/iOS/Before/XiaoYing-2018-07-30-114612.ips was removed!
/Users/zhulixin/Desktop/python-appium/Log/CrashInfo/iOS/Before/XiaoYing-2018-07-30-162434.ips was removed!
Directory: /Users/zhulixin/Desktop/python-appium/Log/CrashInfo/iOS/Before/XiaoYing-2018-07-30-162434.ips was removed!
/Users/zhulixin/Desktop/python-appium/Log/CrashInfo/iOS/Before/XiaoYing-2018-09-04-102545.ips was removed!
Directory: /Users/zhulixin/Desktop/python-appium/Log/CrashInfo/iOS/Before/XiaoYing-2018-09-04-102545.ips was removed!
Directory: /Users/zhulixin/Desktop/python-appium/Log/CrashInfo/iOS/Before/com.apple.appstored was removed!
/Users/zhulixin/Desktop/python-appium/Log/CrashInfo/iOS/Before/XiaoYing-2018-07-30-184609.ips was removed!
......
......

Process finished with exit code 0

11.最终 log 输出信息及路径

12.报告输出

1.Android

2.iOS

运行环境

  1. Windows 7 及以上 / OSX
  2. Android SDK 的执行环境
  3. python3.x
  4. Appium 1.7.x 及以上

代码获取

最新的稳定代码会推送到 github 上,直接 clone 即可使用。

git@github.com:Lemonzhulixin/python-appium.git

执行注意事项

1.安装包路径指定:Base.BaseInit

apkPath = PATH("../app/VivaVideo_7.2.5.apk")  # 测试的app路径
ipaPath = PATH("../app/xiaoying.ipa")  # 测试的app路径

2.为了避免同一台 PC 上同时连接 android 和 iOS 设备时,获取设备问题,将 runner 文件两个平台分开处理

Android执行: python3 ../Runner/runner.py
iOS执行:python3 ../Runner/runner_iOS.py

3.在过滤待测 app crashreport 时,记得在 runner_iOS.py 中修改待测 app crashreport 文件关键字

find_str = 'XiaoYing-'  # 待测app crashreport文件关键字

4.过滤待测 app 系统日志,修改待测 app 关键字,如此处的'XiaoYing'

#获取系统日志,过滤当前app的log,如不需要获取系统日志,注掉即可
syslog_path = os.path.join(PATH("../Log/CrashInfo/iOS/"), "syslog.log")
sys_cmd = 'idevicesyslog -u ' + get_phone["udid"] + " |grep 'XiaoYing' > %s" % (syslog_path)
os.popen(sys_cmd)

目前的遗留问题

  • email 邮件发送尚未调试
  • 多设备执行还有点问题
  • 当遇到有些用例比较麻烦,必须单独写 page 层
  • 因为对 python 的 map 方法不是很懂,所以控件集参数化用了最原始,最笨的字符串替换,输出一个临时 temp.yaml 文件,测试完成后再删除;如果有对 map 熟悉的同学,欢迎帮忙写个方法来处理,感谢!

后续计划

  • 测试数据 DB 存储
  • 结果集分析
共收到 31 条回复 时间 点赞

多谢分享,赞一个~~

获取不了,没有权限

windows 上执行编码有点问题呀,cmd 执行后返回的数据不是 utf-8 编码

jeky2017 回复

谢谢~🙏

回复

再试试,没有设置权限,public 的,其他人好像都可以;再不行的话你就 fork 一个

雨夜狂奔 回复

windows 的代码我没动过,暂时没 windows 的 pc,不过这个问题,谷歌一下应该不难解决。

只是提出来一下,要在 windows 上执行还是要改一些东西的,不过也还好

还是不行。报错

Cloning into 'python-appium'...
git@github.com: Permission denied (publickey).
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.
回复

我其他的你可以 clone 吗?不行的话你留个 qq,我加你~

qq79523822

selenium.common.exceptions.WebDriverException: Message: An unknown server-side error occurred while processing the command. Original error: Could not proxy command to remote server. Original error: Error: read ECONNRESET

运行成功一次后报这个错误,找不到解决方法。macos,appium1.9.
降级为 appium1.8.0 后问题消失,请问下作者用的版本是多少,有解决方法吗

秋了秋天 回复

我是 python3.6+appium1.8.1, 1.9.x 要是有问题,就先用 1.8.x 的好了。暂时还不没升级到 1.9,你自己先调试看看

仅楼主可见
purplerain 回复

已发送到你 QQ 邮箱,互相学习😀

仅楼主可见

大佬们插楼 我能问下 appium 如何定位 ios 的 toast 吗

zhenwang 回复

iOS 的没研究过,好像不太行,我们一般不验证这种 toast。可以尝试截图查看应该也行。

动作封装的方法看不明白,clear() 方法找不到

仅楼主可见

我这边 git 也拉不下来,有权限限制楼主大大可以发给我 qq 一下吗😀 qq:877106179

仅楼主可见

运行的时候报了以下的错误:
self.elements_by(mOperate).click()
AttributeError: 'function' object has no attribute 'click'

23楼 已删除
xiaosatester 回复

你好,你解决这个问题了吗?

楼主好,出现这个问题,
self.elements_by(mOperate).click()
AttributeError: 'function' object has no attribute 'click'
但是实际上看 driver 已经传过去,打印的 driver 如下

王志国 回复

你好,我也遇到这个问题了,你这边解决了吗

剑玄 回复

俺也一样

niceqwer555 回复

这个问题解决了吗,我也遇到同样的问题了

@lemon
self.elements_by(mOperate).click()
AttributeError: 'function' object has no attribute 'click'

运行会报这个错误,我看大家都会碰到,你知道这个是什么原因吗

王志国 回复

你好 这个问题最后你解决了吗

搞定了,看起来是 BaseOperate.py 里面的这个方法的问题:elements_by(self, mOperate),我把代码改造了一下就可以跑通过了,大家也可以试下

一直没搞懂,请教楼主大大,为啥入口要套件调度套件,只用一层试了效果也是一样的
suite = unittest.TestSuite()
suite.addTest(ParametrizedTestCase.parametrize(HomeTest, param=devices))

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