Appium混合APP坑汇总

我司APP大部分页面Android和iOS的客户端只提供了webview的功能,都是由H5处理业务逻辑和用户交互。H5承担了和服务端、和客户端的交互。

因业务需要,需针对部分功能做APP自动化,执行过程中遇到了一些坑,特此记录,用以备忘。

1、前提条件:(以mac系统为例)

1)安装brew

ruby -e "$(curl -fsSL https://raw.github.com/mxcl/homebrew/go)"

2)安装JDK

3)安装python3、pycharm及相关包

brew install python3
pip install selenium
pip install appium-python-client

4)配置cnpm

npm install -g cnpm --registry=https://registry.npm.taobao.org

5)安装git/nodejs

brew install git
brew install nodejs

5)安装appium

sudo cnpm install appium -g

6)安装appium-doctor

sudo cnpm install appium-doctor -g

7)appium-doctor监测后安装相关包

8)安装android sdk并配置环境

9)安装appium-desktop

https://github.com/appium/appium-desktop/releases/

10)安装android-studio并配置

11)下载Genymotion模拟器

2、框架选择

pyapp代码:

from lib.pyse import Pyse
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from appium.webdriver.common.mobileby import MobileBy
from appium.webdriver.connectiontype import ConnectionType
from lib.logger import logger

class Pyapp(Pyse):
    def __init__(self, driver):
        self.d = driver

    @property
    def driver(self):
        return self.d

    def swipe_up(self, t=500, n=1):
        """向上滑动屏幕"""
        l = self.d.get_window_size()
        x1 = l['width'] * 0.5  # x坐标
        y1 = l['height'] * 0.85  # 起始y坐标
        y2 = l['height'] * 0.25  # 终点y坐标
        for i in range(n):
            self.d.swipe(x1, y1, x1, y2, t)

    def swipe_down(self, t=500, n=1):
        """向下滑动屏幕"""
        l = self.d.get_window_size()
        x1 = l['width'] * 0.5  # x坐标
        y1 = l['height'] * 0.25  # 起始y坐标
        y2 = l['height'] * 0.75  # 终点y坐标
        for i in range(n):
            self.d.swipe(x1, y1, x1, y2, t)

    def swipe_left(self, t=500, n=1):
        """向左滑动屏幕"""
        l = self.d.get_window_size()
        x1 = l['width'] * 0.75
        y1 = l['height'] * 0.5
        x2 = l['width'] * 0.05
        for i in range(n):
            self.d.swipe(x1, y1, x2, y1, t)

    def swipe_right(self, t=500, n=1):
        """向右滑动屏幕"""
        l = self.d.get_window_size()
        x1 = l['width'] * 0.05
        y1 = l['height'] * 0.5
        x2 = l['width'] * 0.75
        for i in range(n):
            self.d.swipe(x1, y1, x2, y1, t)

    def get_element(self, css):
        if "=>" not in css:
            raise NameError("Positioning syntax errors, lack of '=>'.")

        self.element_wait(css)

        by = css.split("=>")[0]
        value = css.split("=>")[1]

        if by == "id":
            element = self.d.find_element_by_id(value)
        elif by == "name":
            element = self.d.find_element_by_name(value)
        elif by == "class":
            element = self.d.find_element_by_class_name(value)
        elif by == "link_text":
            element = self.d.find_element_by_link_text(value)
        elif by == "xpath":
            element = self.d.find_element_by_xpath(value)
        elif by == "css":
            element = self.d.find_element_by_css_selector(value)
        elif by == 'content':
            element = self.d.find_element_by_accessibility_id(value)
        elif by == 'android':
            element = self.d.find_element_by_android_uiautomator(value)
        elif by == 'ios':
            element = self.d.find_element_by_ios_predicate(value)
        else:
            raise NameError(
                "Please enter the correct targeting elements,'id','name','class','link_text','xpath','css'.")
        return element

    def get_elements(self, css):
        if "=>" not in css:
            raise NameError("Positioning syntax errors, lack of '=>'.")

        self.element_wait(css)

        by = css.split("=>")[0]
        value = css.split("=>")[1]

        if by == "id":
            elements = self.d.find_elements_by_id(value)
        elif by == "name":
            elements = self.d.find_elements_by_name(value)
        elif by == "class":
            elements = self.d.find_elements_by_class_name(value)
        elif by == "link_text":
            elements = self.d.find_elements_by_link_text(value)
        elif by == "xpath":
            elements = self.d.find_elements_by_xpath(value)
        elif by == "css":
            elements = self.d.find_elements_by_css_selector(value)
        elif by == 'content':
            elements = self.d.find_elements_by_accessibility_id(value)
        elif by == 'android':
            elements = self.d.find_elements_by_android_uiautomator(value)
        elif by == 'ios':
            elements = self.d.find_elements_by_ios_predicate(value)
        else:
            raise NameError(
                "Please enter the correct targeting elements,'id','name','class','link_text','xpath','css'.")
        return elements

    def element_wait(self, css, secs=60):
        if "=>" not in css:
            raise NameError("Positioning syntax errors, lack of '=>'.")

        by = css.split("=>")[0]
        value = css.split("=>")[1]

        if by == "id":
            WebDriverWait(self.d, secs, 1).until(EC.presence_of_element_located((MobileBy.ID, value)))
        elif by == "name":
            WebDriverWait(self.d, secs, 1).until(EC.presence_of_element_located((MobileBy.NAME, value)))
        elif by == "class":
            WebDriverWait(self.d, secs, 1).until(EC.presence_of_element_located((MobileBy.CLASS_NAME, value)))
        elif by == "link_text":
            WebDriverWait(self.d, secs, 1).until(EC.presence_of_element_located((MobileBy.LINK_TEXT, value)))
        elif by == "xpath":
            WebDriverWait(self.d, secs, 1).until(EC.presence_of_element_located((MobileBy.XPATH, value)))
        elif by == "css":
            WebDriverWait(self.d, secs, 1).until(EC.presence_of_element_located((MobileBy.CSS_SELECTOR, value)))
        elif by == "content":
            WebDriverWait(self.d, secs, 1).until(
                EC.presence_of_element_located((MobileBy.ACCESSIBILITY_ID, value)))
        elif by == 'android':
            WebDriverWait(self.d, secs, 1).until(
                EC.presence_of_element_located((MobileBy.ANDROID_UIAUTOMATOR, value)))
        elif by == 'ios':
            WebDriverWait(self.d, secs, 1).until(
                EC.presence_of_element_located((MobileBy.IOS_PREDICATE, value)))
        else:
            raise NameError(
                "Please enter the correct targeting elements,'id','name','class','link_text','xpath','css'.")

    def random_action(self, css):
        if "=>" not in css:
            raise NameError("Positioning syntax errors, lack of '=>'.")

        by = css.split("=>")[0]
        value = css.split("=>")[1]
        try:
            if by == "id":
                return EC.visibility_of_element_located((MobileBy.ID, value))
            elif by == "name":
                return EC.visibility_of_element_located((MobileBy.NAME, value))
            elif by == "class":
                return EC.visibility_of_element_located((MobileBy.CLASS_NAME, value))
            elif by == "link_text":
                return EC.visibility_of_element_located((MobileBy.LINK_TEXT, value))
            elif by == "xpath":
                return EC.visibility_of_element_located((MobileBy.XPATH, value))
            elif by == "css":
                return EC.visibility_of_element_located((MobileBy.CSS_SELECTOR, value))
            elif by == "content":
                return EC.visibility_of_element_located((MobileBy.ACCESSIBILITY_ID, value))
            elif by == 'android':
                return EC.visibility_of_element_located((MobileBy.ANDROID_UIAUTOMATOR, value))
            elif by == 'ios':
                return EC.visibility_of_element_located((MobileBy.IOS_PREDICATE, value))
            else:
                raise NameError(
                    "Please enter the correct targeting elements,'id','name','class','link_text','xpath','css'.")
        except Exception as e:
            pass

    def key_code(self, code):
        '''

        :param code: 按键码
        :return:
        '''
        self.d.press_keycode(code)

    def hide_keyboard(self):
        self.d.hide_keyboard()

    def background_app(self, second):
        self.d.background_app(second)

    @property
    def current_context(self):
        return self.d.current_context

    @property
    def context(self):
        return self.d.context

    @property
    def contexts(self):
        return self.d.contexts

    def switch_web_view(self):
        context_names = self.contexts
        if len(context_names) > 1:
            logger.debug('webview context_names is',context_names[-1])
            self.d.switch_to.context(context_names[-1])



    def switch_native_app(self):
        context_names = self.contexts
        self.d.switch_to.context(context_names[0])

    def set_network(self, network):
        '''
        1、WIFI 2、数据流量 3、飞行模式 4、无网 5、全部打开
        '''
        if network == 1:
            self.d.set_network_connection(ConnectionType.WIFI_ONLY)
        elif network == 2:
            self.d.set_network_connection(ConnectionType.DATA_ONLY)
        elif network == 3:
            self.d.set_network_connection(ConnectionType.AIRPLANE_MODE)
        elif network == 4:
            self.d.set_network_connection(ConnectionType.NO_CONNECTION)
        elif network == 5:
            self.d.set_network_connection(ConnectionType.ALL_NETWORK_ON)
        return self

    def launch_app(self):
        return self.d.launch_app()

    def close_app(self):
        return self.d.close_app()

    def reset(self):
        return self.d.reset()

    def is_app_installed(self, package):
        return self.d.is_app_installed(package)

    def set_value(self, element, value):
        self.d.set_value(element, value)

    def lock(self, s):
        self.d.lock(s)

    def wait_and_save_exception(self, css, name):
        try:
            self.element_wait(css, secs=5)
            return True
        except Exception as e:
            from lib.path import APPPICTUREPATH
            import threading
            self.get_windows_img(APPPICTUREPATH.format(threading.current_thread().getName()) + name + '.jpg')
            return False
pyapp

 报告:BeautifulReport

3、编写代码-坑汇总

实际执行过程中发现如下问题,依次记录

1)问题:本次测试使用的模拟器,发现无法成功安装APP

原因:打包时开发去除x86支持,无法在模拟器中成功安装,app/src/AndroidManifest.xml代码中加上x86支持即可

2)问题:使用appium中,无法看到h5页面

原因:安装包未打开远程调试,需要代码中加上

3)问题:appium中看不到登录页面

原因:我司为金融类APP,开发对登录做了安全处理,导致无法看到,在代码中注释相关代码即解决

代码注释截图:(找到对应的LoginActivity类)

4)h5定位方式:

①获取webview,切换到h5(调用Pyapp模块中的switch_web_view方法)

②h5元素直接使用selenium定位方式

③之后切换回app(调用Pyapp模块中的switch_native_app方法)

5)图片验证:

原理可参照之前的博客:极验验证破解

 

posted @ 2021-06-11 16:33  Co丶cc  阅读(119)  评论(0)    收藏  举报