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

Appium+Python+pytest自動化測試框架的實戰(zhàn)

 更新時間:2021年12月23日 09:51:48   作者:蘇世i  
本文主要介紹了Appium+Python+pytest自動化測試框架的實戰(zhàn),文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下

菜鳥一枚,寫的不好勿噴,大家一起學(xué)習

先簡單介紹一下目錄,再貼一些代碼,代碼里有注釋

Basic目錄下寫的是一些公共的方法,Data目錄下寫的是測試數(shù)據(jù),image存的是測試失敗截圖,Log日志文件,Page測試的定位元素,report測試報告,Test測試用例,pytest.ini是pytest啟動配置文件,requirements.txt需要安裝的py模塊,run.py運行文件

在這里插入圖片描述

Basic/base.py

里面封裝了 一些方法,元素的點擊,輸入,查找,還有一些自己需要的公共方法也封裝在里面,如果你們有別的需要可以自己封裝調(diào)用

# coding=utf-8
import random
import allure
import pymysql
import time
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from Basic import Log
import os

log = Log.MyLog()


class Base(object):
    def __init__(self, driver):
        self.driver = driver

    # 自定義一個元素查找方法
    def find_element(self, feature,timeout=5, poll=1.0):
        # feature = By.XPATH,"http://*[@text='顯示']"
        """
        依據(jù)用戶傳入的元素信息特征,然后返回當前用戶想要查找元素
        :param feature: 元組類型,包含用戶希望的查找方式,及該方式對應(yīng)的值
        :return: 返回當前用戶查找的元素
        """
        by = feature[0]
        value = feature[1]
        wait = WebDriverWait(self.driver, timeout, poll)
        if by == By.XPATH:
            # print( "說明了用戶想要使用 xpath 路徑的方式來獲取元素" )
            value = self.make_xpath(value)
        return wait.until(lambda x: x.find_element(by,value))

    def find_elements(self, feature):
        wait = WebDriverWait(self.driver, 5, 1)
        return wait.until(lambda x: x.find_elements(feature[0], feature[1]))

    def click_element(self, loc):
        '''
            封裝點擊操作函數(shù)
        '''
        self.find_element(loc).click()

    def input_text(self, loc, text):
        '''
            封裝輸入操作函數(shù)
        '''
        self.fm = self.find_element(loc)
        self.fm.clear()  # 需要先清空輸入框,防止有默認內(nèi)容
        self.fm.send_keys(text)

    # 自定義了一個可以自動幫我們拼接 xpath 路徑的工具函數(shù)
    def make_xpath(self, feature):
        start_path = "http://*["
        end_path = "]"
        res_path = ""

        if isinstance(feature, str):

            # 如果是字符串 我們不能直接上來就拆我們可以判斷一下它是否是默認正確的 xpath 寫法
            if feature.startswith("http://*["):
                return feature

            # 如果用戶輸入的是字符串,那么我們就拆成列表再次進行判斷
            split_list = feature.split(",")
            if len(split_list) == 2:
                # //*[contains(@text,'設(shè)')]
                res_path = "%scontains(@%s,'%s')%s" % (start_path, split_list[0], split_list[1], end_path)
            elif len(split_list) == 3:
                # //[@text='設(shè)置']
                res_path = "%s@%s='%s'%s" % (start_path, split_list[0], split_list[1], end_path)
            else:
                print("請按規(guī)則使用")
        elif isinstance(feature, tuple):
            for item in feature:
                # 默認用戶在元組當中定義的數(shù)據(jù)都是字符串
                split_list2 = item.split(',')
                if len(split_list2) == 2:
                    res_path += "contains(@%s,'%s') and " % (split_list2[0], split_list2[1])
                elif len(split_list2) == 3:
                    res_path += "@%s='%s' and " % (split_list2[0], split_list2[1])
                else:
                    print("請按規(guī)則使用")
            andIndex = res_path.rfind(" and")
            res_path = res_path[0:andIndex]
            res_path = start_path + res_path + end_path
        else:
            print("請按規(guī)則使用")

        return res_path

    def assert_ele_in(self, text, element):
        '''
            封裝斷言操作函數(shù)
        '''
        try:
            assert text in self.find_element(element).text
            assert 0
        except Exception:
            assert 1

    def get_assert_text(self, element):
        ele = self.find_element(element, timeout=5, poll=0.1)
        return ele.text

    # 自定義一個獲取 toast內(nèi)容的方法
    def get_toast_content(self, message):
        tmp_feature = By.XPATH, "http://*[contains(@text,'%s')]" % message
        ele = self.find_element(tmp_feature)
        return ele.text

    # 自定義一個工具函數(shù),可以接收用戶傳遞的部分 toast 信息,然后返回一個布爾值,來告訴
    # 用戶,目標 toast 到底是否存在
    def is_toast_exist(self, mes):
        # 拿著用戶傳過來的 message 去判斷一下包含該內(nèi)容的 toast 到底是否存在。
        try:
            self.get_toast_content(mes)
            return True
        except Exception:
            # 如果目標 toast 不存在那么就說明我們的實際結(jié)果和預(yù)期結(jié)果不一樣
            # 因此我們想要的是斷言失敗
            return False

    def get_mysql(self,  table, value):
        '''連接數(shù)據(jù)庫'''
        # 打開數(shù)據(jù)庫連接
        db = pymysql.connect(host='', port=, db=, user='', passwd='', charset='utf8')
        # 使用 cursor() 方法創(chuàng)建一個游標對象 cursor
        cursor = db.cursor()
        try:
            # 使用 execute()  方法執(zhí)行 SQL 查詢
            cursor.execute(value)
            db.commit()
        except Exception as e:
            print(e)
            db.rollback()
        # 使用 fetchone() 方法獲取單條數(shù)據(jù).
        data = cursor.fetchone()
        # 關(guān)閉數(shù)據(jù)庫連接
        db.close()
        return data

    def get_xpath(self, value):
        '''封裝獲取xpath方法'''
        text = By.XPATH, '//*[@text="%s"]' % value
        return text

    # 自定義一個獲取當前設(shè)備尺寸的功能
    def get_device_size(self):
        x = self.driver.get_window_size()["width"]
        y = self.driver.get_window_size()["height"]
        return x, y

    # 自定義一個功能,可以實現(xiàn)向左滑屏操作。
    def swipe_left(self):
        start_x = self.get_device_size()[0] * 0.9
        start_y = self.get_device_size()[1] * 0.5
        end_x = self.get_device_size()[0] * 0.4
        end_y = self.get_device_size()[1] * 0.5
        self.driver.swipe(start_x, start_y, end_x, end_y)

    # 自定義一個功能,可以實現(xiàn)向上滑屏操作。
    def swipe_up(self):
        start_x = self.get_device_size()[0] * 1/2
        start_y = self.get_device_size()[1] * 1/2
        end_x = self.get_device_size()[0] * 1/2
        end_y = self.get_device_size()[1] * 1/7
        self.driver.swipe(start_x, start_y, end_x, end_y, 500)

    # 切換到微信
    def switch_weixxin(self):
        self.driver.start_activity("com.tencent.mm", ".ui.LauncherUI")

    # 切換到醫(yī)生端
    def switch_doctor(self):
        self.driver.start_activity("com.rjjk_doctor", ".MainActivity")

    # 切換到銷售端
    def switch_sale(self):
        self.driver.start_activity("com.rjjk_sales", ".MainActivity")

    def switch_webview(self):
        # 切換到webview
        print(self.driver.contexts)
        time.sleep(5)
        self.driver.switch_to.context("WEBVIEW_com.tencent.mm:tools")
        print("切換成功")
        time.sleep(3)

    # 自定義根據(jù)坐標定位
    def taptest(self, a, b):
        # 設(shè)定系數(shù),控件在當前手機的坐標位置除以當前手機的最大坐標就是相對的系數(shù)了
        # 獲取當前手機屏幕大小X,Y
        X = self.driver.get_window_size()['width']
        Y = self.driver.get_window_size()['height']
        # 屏幕坐標乘以系數(shù)即為用戶要點擊位置的具體坐標
        self.driver.tap([(a * X, b * Y)])

    # 自定義截圖函數(shù)
    def take_screenShot(self):
        '''
        測試失敗截圖,并把截圖展示到allure報告中
        '''
        tm = time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime(time.time()))
        self.driver.get_screenshot_as_file(
            os.getcwd() + os.sep + "image/%s.png" % tm)
        allure.attach.file(os.getcwd() + os.sep + "image/%s.png" %
                           tm, attachment_type=allure.attachment_type.PNG)

    # 自定義隨機生成11位手機號
    def create_phone(self):
        # 第二位數(shù)字
        second = [3, 4, 5, 7, 8][random.randint(0, 4)]
        # 第三位數(shù)字
        third = {
            3: random.randint(0, 9),
            4: [5, 7, 9][random.randint(0, 2)],
            5: [i for i in range(10) if i != 4][random.randint(0, 8)],
            7: [i for i in range(10) if i not in [4, 9]][random.randint(0, 7)],
            8: random.randint(0, 9),
        }[second]
        # 最后八位數(shù)字
        suffix = random.randint(9999999, 100000000)
        # 拼接手機號
        return "1{}{}{}".format(second, third, suffix)

Basic/deiver.py
APP啟動的前置條件,一個是普通的app,一個是微信公眾號,配置微信公眾號自動化測試和一般的APP是有點區(qū)別的,微信需要切換webview才能定位到公眾號

from appium import webdriver


def init_driver():
    desired_caps = {}
    # 手機 系統(tǒng)信息
    desired_caps['platformName'] = 'Android'
    desired_caps['platformVersion'] = '9'
    # 設(shè)備號
    desired_caps['deviceName'] = 'emulator-5554'
    # 包名
    desired_caps['appPackage'] = ''
    # 啟動名
    desired_caps['appActivity'] = ''
    desired_caps['automationName'] = 'Uiautomator2'
    # 允許輸入中文
    desired_caps['unicodeKeyboard'] = True
    desired_caps['resetKeyboard'] = True
    desired_caps['autoGrantPermissions'] = True
    desired_caps['noReset'] = False
    # 手機驅(qū)動對象
    driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", desired_caps)
    return driver


def driver_weixin():
    desired_caps = {}
    # 手機 系統(tǒng)信息
    desired_caps['platformName'] = 'Android'
    desired_caps['platformVersion'] = '9'
    # 設(shè)備號
    desired_caps['deviceName'] = ''
    # 包名
    desired_caps['appPackage'] = 'com.tencent.mm'
    # 啟動名
    desired_caps['appActivity'] = '.ui.LauncherUI'
    # desired_caps['automationName'] = 'Uiautomator2'
    # 允許輸入中文
    desired_caps['unicodeKeyboard'] = True
    desired_caps['resetKeyboard'] = True
    desired_caps['noReset'] = True
    # desired_caps["newCommandTimeout"] = 30
    # desired_caps['fullReset'] = 'false'
    # desired_caps['newCommandTimeout'] = 10
    # desired_caps['recreateChromeDriverSessions'] = True
    desired_caps['chromeOptions'] = {'androidProcess': 'com.tencent.mm:tools'}
    # 手機驅(qū)動對象
    driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", desired_caps)
    return driver

Basic/get_data.py
這是獲取測試數(shù)據(jù)的方法

import os
import yaml


def getData(funcname, file):
    PATH = os.getcwd() + os.sep

    with open(PATH + 'Data/' + file + '.yaml', 'r', encoding="utf8") as f:
        data = yaml.load(f, Loader=yaml.FullLoader)

    # 1 先將我們獲取到的所有數(shù)據(jù)都存放在一個變量當中
    tmpdata = data[funcname]

    # 2 所以此時我們需要使用循環(huán)走進它的內(nèi)心。
    res_arr = list()
    for value in tmpdata.values():
        tmp_arr = list()
        for j in value.values():
            tmp_arr.append(j)

        res_arr.append(tmp_arr)

    return res_arr

Basic/Log.py
日志文件,不多介紹

# -*- coding: utf-8 -*-

"""
封裝log方法

"""

import logging
import os
import time

LEVELS = {
    'debug': logging.DEBUG,
    'info': logging.INFO,
    'warning': logging.WARNING,
    'error': logging.ERROR,
    'critical': logging.CRITICAL
}

logger = logging.getLogger()
level = 'default'


def create_file(filename):
    path = filename[0:filename.rfind('/')]
    if not os.path.isdir(path):
        os.makedirs(path)
    if not os.path.isfile(filename):
        fd = open(filename, mode='w', encoding='utf-8')
        fd.close()
    else:
        pass


def set_handler(levels):
    if levels == 'error':
        logger.addHandler(MyLog.err_handler)
    logger.addHandler(MyLog.handler)


def remove_handler(levels):
    if levels == 'error':
        logger.removeHandler(MyLog.err_handler)
    logger.removeHandler(MyLog.handler)


def get_current_time():
    return time.strftime(MyLog.date, time.localtime(time.time()))


class MyLog:
    path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    log_file = path+'/Log/log.log'
    err_file = path+'/Log/err.log'
    logger.setLevel(LEVELS.get(level, logging.NOTSET))
    create_file(log_file)
    create_file(err_file)
    date = '%Y-%m-%d %H:%M:%S'

    handler = logging.FileHandler(log_file, encoding='utf-8')
    err_handler = logging.FileHandler(err_file, encoding='utf-8')

    @staticmethod
    def debug(log_meg):
        set_handler('debug')
        logger.debug("[DEBUG " + get_current_time() + "]" + log_meg)
        remove_handler('debug')

    @staticmethod
    def info(log_meg):
        set_handler('info')
        logger.info("[INFO " + get_current_time() + "]" + log_meg)
        remove_handler('info')

    @staticmethod
    def warning(log_meg):
        set_handler('warning')
        logger.warning("[WARNING " + get_current_time() + "]" + log_meg)
        remove_handler('warning')

    @staticmethod
    def error(log_meg):
        set_handler('error')
        logger.error("[ERROR " + get_current_time() + "]" + log_meg)
        remove_handler('error')

    @staticmethod
    def critical(log_meg):
        set_handler('critical')
        logger.error("[CRITICAL " + get_current_time() + "]" + log_meg)
        remove_handler('critical')


if __name__ == "__main__":
    MyLog.debug("This is debug message")
    MyLog.info("This is info message")
    MyLog.warning("This is warning message")
    MyLog.error("This is error")
    MyLog.critical("This is critical message")


Basic/Shell.py
執(zhí)行shell語句方法

# -*- coding: utf-8 -*-
# @Time    : 2018/8/1 下午2:54
# @Author  : WangJuan
# @File    : Shell.py

"""
封裝執(zhí)行shell語句方法

"""

import subprocess


class Shell:
    @staticmethod
    def invoke(cmd):
        output, errors = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
        o = output.decode("utf-8")
        return o

Page/page.py

class Page:
    def __init__(self, driver):
        self.driver = driver

    @property
    def initloginpage(self):
        return Login_Page(self.driver)

Test/test_login.py
登陸的測試用,我貼一條使用數(shù)據(jù)文件的用例

class Test_login:
    @pytest.mark.parametrize("args", getData("test_login_error", 'data_error_login'))
    def test_error_login(self, args):
        """錯誤登陸"""
        self.page.initloginpage.input_user(args[0])
        self.page.initloginpage.input_pwd(args[1])
        self.page.initloginpage.click_login()
        toast_status = self.page.initloginpage.is_toast_exist(args[2])
        if toast_status == False:
            self.page.initpatientpage.take_screenShot()
            assert False

pytest.ini
pytest配置文件,注釋的是啟動失敗重試3次,因為appium會因為一些不可控的原因失敗,所有正式運行腳本的時候需要加上這個

[pytest]
;addopts = -s --html=report/report.html --reruns 3
addopts = -s --html=report/report.html
testpaths = ./Test
python_files = test_*.py
python_classes = Test*
python_functions = test_add_prescription_list



requirements.txt
框架中需要的患教,直接pip install -r requirements.txt 安裝就可以了,可能會失敗,多試幾次

```python
adbutils==0.3.4
allure-pytest==2.7.0
allure-python-commons==2.7.0
Appium-Python-Client==0.46
atomicwrites==1.3.0
attrs==19.1.0
certifi==2019.6.16
chardet==3.0.4
colorama==0.4.1
coverage==4.5.3
decorator==4.4.0
deprecation==2.0.6
docopt==0.6.2
enum34==1.1.6
facebook-wda==0.3.4
fire==0.1.3
humanize==0.5.1
idna==2.8
importlib-metadata==0.18
logzero==1.5.0
lxml==4.3.4
more-itertools==7.1.0
namedlist==1.7
packaging==19.0
Pillow==6.1.0
pluggy==0.12.0
progress==1.5
py==1.8.0
PyMySQL==0.9.3
pyparsing==2.4.0
pytest==5.0.0
pytest-cov==2.7.1
pytest-html==1.21.1
pytest-metadata==1.8.0
pytest-repeat==0.8.0
pytest-rerunfailures==7.0
PyYAML==5.1.1
requests==2.22.0
retry==0.9.2
selenium==3.141.0
six==1.12.0
tornado==6.0.3
uiautomator2==0.3.3
urllib3==1.25.3
wcwidth==0.1.7
weditor==0.2.3
whichcraft==0.6.0
zipp==0.5.1

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

相關(guān)文章

  • Python cookbook(字符串與文本)針對任意多的分隔符拆分字符串操作示例

    Python cookbook(字符串與文本)針對任意多的分隔符拆分字符串操作示例

    這篇文章主要介紹了Python cookbook(字符串與文本)針對任意多的分隔符拆分字符串操作,結(jié)合實例形式分析了Python使用split()及正則表達式進行字符串拆分操作相關(guān)實現(xiàn)技巧,需要的朋友可以參考下
    2018-04-04
  • Python fileinput模塊使用實例

    Python fileinput模塊使用實例

    這篇文章主要介紹了Python fileinput模塊使用實例,fileinput模塊可以遍歷文本文件的所有行,本文就給出它的使用代碼實例,需要的朋友可以參考下
    2015-05-05
  • OpenCV-Python實現(xiàn)多模板匹配

    OpenCV-Python實現(xiàn)多模板匹配

    模板匹配就是在一幅圖像中尋找另一幅模板圖像最匹配,本文主要實現(xiàn)了多模板匹配,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-06-06
  • Python中的進制轉(zhuǎn)換詳解

    Python中的進制轉(zhuǎn)換詳解

    這篇文章主要介紹了Python中的進制轉(zhuǎn)換詳解,python內(nèi)置函數(shù)進行進制轉(zhuǎn)換的原理是將讀入的一個字符串(python默認讀入的都是字符串形式)轉(zhuǎn)為10進制,然后再用相關(guān)函數(shù)進行其他進制轉(zhuǎn)換,需要的朋友可以參考下
    2023-08-08
  • 使用Python操作Jenkins的過程詳解

    使用Python操作Jenkins的過程詳解

    借助于Python中的python-jenkins模塊,我們可以輕松地編寫腳本來連接到Jenkins服務(wù)器,并執(zhí)行各種操作,如創(chuàng)建、刪除、構(gòu)建Jobs等,這種自動化的方式不僅提高了效率,還使得CI/CD流程更加靈活和可控,本文介紹如何使用Python操作Jenkins的相關(guān)資料,需要的朋友可以參考下
    2024-05-05
  • python讀取mat文件中的struct問題

    python讀取mat文件中的struct問題

    這篇文章主要介紹了python讀取mat文件中的struct問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-07-07
  • python基礎(chǔ)知識(一)變量與簡單數(shù)據(jù)類型詳解

    python基礎(chǔ)知識(一)變量與簡單數(shù)據(jù)類型詳解

    這篇文章主要介紹了python變量與簡單數(shù)據(jù)類型詳解,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習或者工作具有一定的參考學(xué)習價值,需要的朋友們下面隨著小編來一起學(xué)習學(xué)習吧
    2019-04-04
  • python獲取元素在數(shù)組中索引號的方法

    python獲取元素在數(shù)組中索引號的方法

    這篇文章主要介紹了python獲取元素在數(shù)組中索引號的方法,實例分析了Python中index方法的相關(guān)使用技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-07-07
  • 淺析python中的set類型

    淺析python中的set類型

    這篇文章主要介紹了python中的set類型,本文給大家介紹的非常詳細,對大家的學(xué)習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-06-06
  • 解決Python找不到ssl模塊問題 No module named _ssl的方法

    解決Python找不到ssl模塊問題 No module named _ssl的方法

    這篇文章主要介紹了解決Python找不到ssl模塊問題 No module named _ssl的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-04-04

最新評論

彝良县| 公主岭市| 济源市| 廉江市| 馆陶县| 萍乡市| 平江县| 隆林| 兴国县| 普宁市| 津南区| 新巴尔虎左旗| 龙陵县| 廊坊市| 河曲县| 孙吴县| 凤山县| 乐都县| 大化| 罗源县| 东明县| 长沙市| 崇信县| 奉节县| 常熟市| 巴里| 遂溪县| 南靖县| 伊春市| 虞城县| 磐石市| 青铜峡市| 札达县| 南开区| 常德市| 台中县| 九江县| 莫力| 黑龙江省| 合江县| 黄龙县|