最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

python和Appium移動端多設(shè)備自動化測試框架實現(xiàn)

 更新時間:2022年04月26日 10:24:23   作者:888米兔  
這篇文章主要介紹了python和Appium移動端多設(shè)備自動化測試框架實現(xiàn),基于pytest和Appium框架,支持Android和iOS功能自動化的測試框架的相關(guān)內(nèi)容,需要的小伙伴可以參考一下

前言:

本篇文章主要介紹基于pytest和Appium框架,支持Android和iOS功能自動化的測試框架。同時該框架支持多設(shè)備測試,并利用allure庫,生成可視化測試報告。本框架主要涉及的內(nèi)容包括:python3、pytest、appium、allure等,此處已假設(shè)你具備相應(yīng)的基礎(chǔ)知識,同時已有可以隨時運行的測試環(huán)境(iOS設(shè)備的測試只能在Mac系統(tǒng)中執(zhí)行,沒有Mac的朋友們,可以看看不執(zhí)行)

一、流程圖

本部分內(nèi)容先從自動化測試的整體流程開始介紹,目的是希望大家在開始動手去實現(xiàn)框架之前,對測試過程做到清晰明了,這樣在實現(xiàn)過程中,才能幫助我們無論何時,都不會迷茫和不知所措。才能讓我們知道從何開始,如何優(yōu)化以及拓展。

那么我們先來看下面這張流程圖: 

以上是本文所介紹框架的核心流程圖,上圖已經(jīng)展現(xiàn)了框架的核心流程,所以在接下來的講述中,大家可以參考該圖進(jìn)行理解和優(yōu)化。

二、appium服務(wù)

在開始我們的測試之前,還有很多的工作需要我們?nèi)ヌ幚?,這其中最重要,也是我們開始的第一步,就是開啟appium的本地服務(wù)。關(guān)于appium的實現(xiàn)原理,本文不作過多的講解,小編會抽空進(jìn)行補(bǔ)充,屆時也希望大家能及時關(guān)注。心急的小伙伴也可以自行百度哦~這里僅介紹啟動服務(wù)的方法。

根據(jù)appium官方的介紹,我們可以通過下面的方式來啟動appium服務(wù):

/usr/local/bin/appium -a ip -p port

也就是我們在啟動appium時,指定ip和端口,一般來說,本地ip使用127.0.0.1即可,官方默認(rèn)端口為4723,我們也可以修改成自己想要的端口,只要保證使用的端口沒有被其他服務(wù)占用即可。(小技巧:如果你不知道自己appium安裝路徑,可通過which appium來幫你找到)

啟動服務(wù)之后,一般我們可以通過訪問這個連接來驗證服務(wù)是否正常:http://127.0.0.1:4723/wd/hub/status??烧TL問并返回json格式數(shù)據(jù)時,則說明服務(wù)已正常啟動。

但事實上,并不是每次啟動都可以順利進(jìn)行,總會有一些意外的情況發(fā)生。比如說端口被占用。遇到這種情況我們也不必驚慌,做好應(yīng)對即可。那么今天我們就上述的過程結(jié)合python,把它實現(xiàn)出來。

上面的過程,用python來實現(xiàn),其實很簡單,我們這里選擇使用python中的subprocess庫來執(zhí)行命令,從而達(dá)到我們預(yù)期。

代碼片段如下:

import subprocess
import abc
import socket
class Driver:
	__metaclass__ = abc.ABCMeta
	self._host = '127.0.0.1'
	@abc.abstractmethod
	def connect_appium(self, port, n)
		"""
		待實現(xiàn)的連接設(shè)備方法
		"""
		return
	def start_appium(self, port):
		server = self.get_local_server_path()
        host = readConfig.ReadConfig().get_commend("host")
        log_path = root_path + '/result/log'
        cmd = "%s -a %s -p %s" % (server, host, str(port))
        if self.check_port(int(port)):
            subprocess.Popen(cmd, shell=True, stdout=open('%s/AppiumServer%s.log' % (log_path, port), 'w'))
            log.logger.info('%s/AppiumServer%s.log' % (log_path, port))
        else:
            log.logger.info("關(guān)閉被占用的端口號:%s" % str(port))
            self.kill_appium()
            log.logger.info("端口釋放完畢!啟動Appium-server,端口號:%s" % str(port))
            subprocess.Popen(cmd, shell=True, stdout=open('%s/AppiumServer%s.log' % (log_path, port), 'w'))
            log.logger.info("Appium日志信息存儲地址: %s/AppiumServer%s.log" % (log_path, port))
    def check_port(self, port):
        """
        檢查端口占用情況
        :param port:
        :return:
        """
        try:
            host = local_read_config.get_commend("host")
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            log.logger.info(s.connect((host, port)))
            s.shutdown(2)
        except OSError:
            log.logger.info("端口:%s 可用" % str(port))
            return True
        else:
            log.logger.info("端口:%s 已被占用" % str(port))
            return False

以上代碼,會在啟動appium服務(wù)之前,通過socket檢查本地端口是否被占用,若被占用,則先釋放端口,然后再啟動服務(wù),否則直接啟動服務(wù)。

至此,服務(wù)啟動完成,接下來就可以開始連接測試設(shè)備。

三、連接測試設(shè)備

當(dāng)我們啟動好appium服務(wù)后,就可以開始鏈接測試設(shè)備了。因為我們要同時支持Android和iOS的設(shè)備,所以我們先來定義一個Driver類,用來封裝一些共有屬性及方法,然后讓Android和iOS分別繼承它。

appium對于設(shè)備的連接,官方給我們提供了詳細(xì)的方法事例:

# Android environment
from appium import webdriver
desired_caps = dict(
    platformName='Android',
    platformVersion='10',
    automationName='uiautomator2',
    deviceName='Android Emulator',
    app=PATH('../../../apps/selendroid-test-app.apk')
)
self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
el = self.driver.find_element_by_accessibility_id('item')
el.click()
# iOS environment
from appium import webdriver
desired_caps = dict(
    platformName='iOS',
    platformVersion='13.4',
    automationName='xcuitest',
    deviceName='iPhone Simulator',
    app=PATH('../../apps/UICatalog.app.zip')
)
self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
el = self.driver.find_element_by_accessibility_id('item')
el.click()

在以上兩個示例中,我們發(fā)現(xiàn),鏈接設(shè)備使用的都是同一個方法,但不同的設(shè)備需要傳入不同的參數(shù),

下面便是鏈接的關(guān)鍵: 

driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)

既然我們找到了共性,那么就可以對該部分內(nèi)容進(jìn)行一番改造,讓它來自動完成一些它可以完成的事情。那么首先,我們來看一下,再鏈接設(shè)備的過程中,我們到底做了些什么。

從上面的代碼不難看出,每臺設(shè)備連接都可以看成兩步:第一步配置連接參數(shù)、第二步請求連接。

那么我們就可以封裝一些類和方法,來完成我們想要分端操作的想法了。其實并不困難,我們可以分別寫兩個類AndroidDriver和IOSDriver,都繼承自Driver,然后實現(xiàn)設(shè)備連接的方法。

具體實現(xiàn)可參考下面的內(nèi)容:

from Driver import Driver
class AndroidDriver(Driver):
    def __init__(self):
        self.driver = None

    def get_desired_caps(self):
        """
        實現(xiàn)繼承的抽象類方法;獲取鏈接設(shè)備的配置信息
        返回設(shè)備配置信息
        :return:desired_caps
        """
        desired_list = []
        package = local_read_config.get_value("ANDROID", "package")
        activity = local_read_config.get_value("ANDROID", "activity")
        devices_info = self.update_devices_info()
        for i in range(len(devices_info)):
            udid = devices_info[i].get("udid")
            device_name = devices_info[i].get("devices_name")
            platform_version = devices_info[i].get("version")
            system_port1 = 8200 + 2 * i
            desired_caps = {
                "platformName": "Android",
                "platformVersion": platform_version,
                "appPackage": package,
                "appActivity": activity,
                "deviceName": device_name,
                "automationName": "uiautomator2",
                "udid": udid,
                "systemPort": system_port1,
                "newCommandTimeout": 3000,
                # "adbExecTimeout": 50000
            }
            desired_list.append(desired_caps)
        return desired_list
    def connect_appium(self, port, n):
        """
        根據(jù)傳入的port,啟動appium服務(wù)
        :param port:
        :param n:
        :return:
        """
        set_adb_path()
        desired_caps = self.get_desired_caps()
        try:
            self.driver = webdriver.Remote("%s:%s/wd/hub" % (super()._remote_url, str(port)), desired_caps[n])
            return self.driver
        except WebDriverException:
            raise WebDriverException
        except ConnectionError:
            raise ConnectionError

上面的方法主要做了兩件事情,首先收集連接設(shè)備需要的desired_caps信息,然后是連接設(shè)備。需要注意的是,因為我們這個框架是支持多個測試設(shè)備同時連接的,所有這里我們把收集到的每臺測試設(shè)備的desired_caps信息放到了一個數(shù)組中,并且在連接設(shè)備的時候,我們通過appium服務(wù)的端口號和數(shù)組下標(biāo)兩個值,來確定,每臺測試設(shè)備連接的appium服務(wù)。

小提示:一個appium服務(wù)無法同時連接多個手機(jī),但是我們希望能同時連接多個測試手機(jī),并且同時在這連接的多個手機(jī)上進(jìn)行測試,所以我們這里啟動了多個appium服務(wù),并指定了每個啟動的服務(wù)端口號。因此我們只需要將端口號和設(shè)備信息對應(yīng)上即可。

至此,啟動服務(wù)和測試設(shè)備連接的實現(xiàn)就結(jié)束了,接下來就是對元素的操作了。那么我們一起來看一下,關(guān)于Element的那些事情。

四、元素封裝

眾所周知,元素的操作依賴于元素查找。

舉個常見的例子:我想百度搜索一個關(guān)鍵詞,那么我首先要找到搜索框,才能輸入關(guān)鍵詞,然后找到搜索按鈕,并點擊搜索。這就是我們要做的。

常見的定位元素的方法有:ID、XPATH、CLASSNAME、NAME、PREDICATE等,selenium提供了對應(yīng)的方法,我們這里也不做過多的封裝,大家可以直接使用,也可以像我這樣,把一些常見的定位方式封裝成一個統(tǒng)一的方法,實現(xiàn)如下:

    def get_element(self, element_id):
        """
        獲取指定頁面的元素路徑數(shù)據(jù)
        :param element_id: 元素ID
        :return: 獲取的元素對象
        """
        element_type = self.page.get(element_id).get("pathType")
        element_value = self.page.get(element_id).get("pathValue")
        element = None
        if element_type == "ID":
            element = self.driver.find_element_by_id(element_value)
        elif element_type == "CLASSNAME":
            element = self.driver.find_element_by_class_name(element_value)
        elif element_type == "XPATH":
            element = self.driver.find_element_by_xpath(element_value)
        elif element_type == "NAME":
            element = self.driver.find_element_by_name(element_value)
        elif element_type == "ACB_ID":
            element = self.driver.find_element_by_accessibility_id(element_value)
        elif element_type == "PREDICATE":
            element = self.driver.find_element_by_ios_predicate(element_value)
        return element

大家自己選擇是否進(jìn)行封裝,正常調(diào)用selenium的方法也是OK噠。

同樣的道理,我們還可以封裝一些常用的操作,比如滑動屏幕,鍵盤操作等。

分端元素操作

因為我們分別接入了Android和iOS,那么它們的操作,各有不同之處,我們可以將各自的特色操作分別集中到一個單獨的AndroidElement類和iOSElement類中,這樣在后面使用的時候,我們直接繼承這兩個類就可以,并且從結(jié)構(gòu)上看,也比較清晰。

比如同樣是滑動屏幕,swipe在Android和iOS系統(tǒng)上的表現(xiàn)就不一致,因此我們就選擇了其他方法:

AndroidElement:

    def swipe_to_up(self):
        """
        向上劃,頁面滾動到最下方
        :return:
        """
        width = self.driver.get_window_size()["width"]
        height = self.driver.get_window_size()["height"]
        self.driver.swipe(width / 2, height * 3 / 5, width / 2, height / 5, duration=500)

iOSElement:

    def swipe_to_up(self):
        """
        向上滑動
        :return:
        """
        self.driver.execute_script('mobile: swipe', {'direction': 'up'})

以上只是一個小例子,只是想說明,如果有這樣的操作差異,我們可以將它們分開處理,這樣會顯得邏輯更清晰。

有了上面的實現(xiàn),我們就只需要寫測試的腳步就可以。寫腳本部分的內(nèi)容就先略過,不做詳細(xì)描述,畢竟不同的業(yè)務(wù)需求場景,都有其獨特的腳本邏輯。凡事萬變不離其宗,元素還是那個元素,操作還是那些操作,就讓大家自己去盡情發(fā)揮吧。

那么,一切準(zhǔn)備就緒,就差讓我們的程序跑起來了。接下來就讓我們來看看,如何讓我們的測試同時在多個連接的測試設(shè)備上進(jìn)行測試。

五、運行

因為我們的測試是通過pytest來執(zhí)行的,所以pytest的所有執(zhí)行參數(shù)都是可以正常使用的。而我們,也只是利用pytest的main函數(shù)來完成本次執(zhí)行。唯一不同的是,為了滿足不同設(shè)備同時進(jìn)行測試,我們?yōu)槊恳慌_設(shè)備的測試,都創(chuàng)建了一個進(jìn)程。每一個進(jìn)程都包含了上述完整的流程。選擇進(jìn)程而非線程的原因也很簡單,相信大家也都知道,進(jìn)程和線程的關(guān)系吧,在同一個進(jìn)程中的線程資源是共享的。而在我們看來,每一臺設(shè)備的測試都應(yīng)該是獨立的、互不干擾的,所以我們選擇進(jìn)程而非線程。

具體實現(xiàn)如下:

from multiprocessing import Process
import pytest
import time
import os, re
import subprocess
from appiums.common import read_files
from appiums.driver.iOSDriver import IOSDriver
from driver.androidDriver import AndroidDriver
from driver import Driver
from elements import Element

class Run(Process):
    def __init__(self, name, args):
        super(Run, self).__init__()
        self.name = name
        self.args = args
        self.root_path = os.getcwd()
        self.device_name = re.sub('[\']', '', str(args[2].get("deviceName")).replace(" ", "_"))
    def run_test(self):
        """
        執(zhí)行測試用例
        :return:
        """
        pytest.main([
                     '--alluredir', '%s/result/data/%s' % (self.root_path, self.device_name)])
        time.sleep(2)
    def generate_report(self):
        """
        整合測試報告到項目根目錄下的result/report目錄下
        :return: none
        """
        cmd = "allure generate %s/result/data/%s -o %s/result/report/%s --clean" \
              % (self.root_path, self.device_name, self.root_path, self.device_name)
        stdout = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, text=True)
        log.logger.info("測試報告查看路徑:%s" % str(stdout.stdout.readlines()[0]).split(" ")[-1][:-1])
    def get_environment_info(self):
        """
        獲取測試環(huán)境的信息
        :return:
        """
        env = {
            "測試平臺": self.args[2].get("platformName"),
            "設(shè)備名稱": self.device_name,
            "設(shè)備系統(tǒng)版本": self.args[2].get("platformVersion"),
            "設(shè)備udid": self.args[2].get("udid"),
            "應(yīng)用名稱": self.args[2].get("bundleId") if str(self.args[2].get("platformName")).lower() == 'ios' else self.args[2].get("appPackage"),
        }
        return env
    def run(self):
        """
        執(zhí)行線程中的任務(wù)
        :return:
        """
        Driver.Driver().start_appium(self.args[0])
        time.sleep(5)
        self.set_driver()
        time.sleep(1)
        self.run_test()
        time.sleep(1)
        read_files.set_environment(self.device_name, self.get_environment_info())
        time.sleep(1)
        self.generate_report()

def main(desired_caps):
    """
    開啟測試進(jìn)程執(zhí)行測試
    """
    list_p = []
    process_num = len(desired_caps)
    if process_num > 0:
        for a in range(process_num):
            port1 = 4723 + 2 * a
            p = Run('測試進(jìn)程-%s' % str(port1), args=(port1, a, desired_caps[a]))
            p.start()
            log.logger.info("設(shè)備%s在進(jìn)程 %s 上進(jìn)行測試, 進(jìn)程ID:%s" % (desired_caps[a].get("deviceName"), p.name, p.pid))
            list_p.append(p)
        for b in list_p:
            b.join()
        Driver.Driver().kill_appium()
    else:
        log.logger.error("沒有設(shè)備可進(jìn)行測試,請重新連接設(shè)備后嘗試!")
        exit(-1)

def android_run():
    caps = AndroidDriver().get_desired_caps()
    main(caps)

def ios_run():
    caps = IOSDriver().get_desired_caps()
    main(caps)

到此這篇關(guān)于python和Appium移動端多設(shè)備自動化測試框架實現(xiàn)的文章就介紹到這了,更多相關(guān)python和Appium自動化內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Python Beautiful Soup模塊使用教程詳解

    Python Beautiful Soup模塊使用教程詳解

    Beautiful Soup 簡稱 BS4(其中 4 表示版本號)是一個 Python 中常用的頁面解析庫,它可以從 HTML 或 XML 文檔中快速地提取指定的數(shù)據(jù),這篇文章主要介紹了Python Beautiful Soup模塊的使用
    2023-02-02
  • python中綁定方法與非綁定方法的實現(xiàn)示例

    python中綁定方法與非綁定方法的實現(xiàn)示例

    本文主要介紹了python中綁定方法與非綁定方法的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • 一文帶你掌握Pyecharts地理數(shù)據(jù)可視化的方法

    一文帶你掌握Pyecharts地理數(shù)據(jù)可視化的方法

    這篇文章主要介紹了一文帶你掌握Pyecharts地理數(shù)據(jù)可視化的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-02-02
  • Python獲取系統(tǒng)默認(rèn)字符編碼的方法

    Python獲取系統(tǒng)默認(rèn)字符編碼的方法

    這篇文章主要介紹了Python獲取系統(tǒng)默認(rèn)字符編碼的方法,涉及Python中sys模塊getdefaultencoding方法的使用技巧,需要的朋友可以參考下
    2015-06-06
  • 深入理解NumPy簡明教程---數(shù)組3(組合)

    深入理解NumPy簡明教程---數(shù)組3(組合)

    本篇文章對NumPy數(shù)組進(jìn)行較深入的探討。首先介紹自定義類型的數(shù)組,接著數(shù)組的組合,最后介紹數(shù)組復(fù)制方面的問題,有興趣的可以了解一下。
    2016-12-12
  • 利用Python批量處理多個txt文本的示例代碼

    利用Python批量處理多個txt文本的示例代碼

    這篇文章主要給大家介紹了關(guān)于如何利用Python批量處理多個txt文本的方法,文中通過實例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下
    2023-10-10
  • Python操作MongoDB數(shù)據(jù)庫的方法示例

    Python操作MongoDB數(shù)據(jù)庫的方法示例

    這篇文章主要介紹了Python操作MongoDB數(shù)據(jù)庫的方法,結(jié)合實例形式分析了Python命令行模式下操作MongoDB數(shù)據(jù)庫實現(xiàn)連接、查找、刪除、排序等相關(guān)操作技巧,需要的朋友可以參考下
    2018-01-01
  • Python 一篇文章看懂時間日期對象

    Python 一篇文章看懂時間日期對象

    學(xué)習(xí)每一門語言多多少少會用到與時間相關(guān)的東西,咱們今天呢就談一談Python中的時間與日期對象。在Python中時間對象一般可以用來測量效率。日期對象用來處理日期與字符串之間的關(guān)系
    2022-03-03
  • Python中八大圖像特效算法的示例詳解

    Python中八大圖像特效算法的示例詳解

    圖像特效處理是基于圖像像素數(shù)據(jù)特征,將原圖像進(jìn)行一定步驟的計算——例如像素作差、灰度變換、顏色通道融合等,從而達(dá)到期望的效果。本文將為大家介紹八種常用的圖像特效算法,需要的可以參考一下
    2022-03-03
  • Python 中包/模塊的 `import` 操作代碼

    Python 中包/模塊的 `import` 操作代碼

    這篇文章主要介紹了Python 中包/模塊的 `import` 操作代碼,非常不錯,具有一定的參考借鑒價值 ,需要的朋友可以參考下
    2019-04-04

最新評論

井研县| 黄陵县| 临海市| 无极县| 黄石市| 综艺| 建德市| 平远县| 枞阳县| 宝山区| 山西省| 九寨沟县| 耿马| 罗平县| 咸宁市| 淮北市| 循化| 宁乡县| 辉南县| 成都市| 潜山县| 上杭县| 历史| 丹东市| 湖南省| 洛南县| 姜堰市| 罗江县| 阿瓦提县| 兰考县| 开江县| 宿迁市| 岢岚县| 招远市| 申扎县| 夏津县| 洪雅县| 陆河县| 芜湖县| 葵青区| 新民市|