8.Appium PO模型實戰

1.po模型概述

我們進行自動化測試時,如果把代碼都寫在一個腳本中,代碼的可讀性會變差,且後期代碼維護也麻煩,最好的想法就是測試對象和測試用例可以分離,可以很快定位問題,代碼可讀性高,也比較容易理解。這裏推薦大家在自動化框架中加入PO模型思想,那什麼是PO模型呢?

所謂的PO就是page object,通俗解釋一下就是每個頁面當成一個對象,給這些頁面寫一個類,主要就是完成元素定位和業務操作。

而在實際的用例書寫中,你就會發小,UI自動化的操作是比較固定的,常用的方法比較固定(點擊、傳入鍵值、截圖等),而不同的是所操作的元素,這樣的話我們只需要傳入不同的元素即可。

詳情請點擊 https://study.163.com/course/introduction/1209833813.htm 查看

整體架構如下:

2.封裝file.py

file.py中存放的是一些公共的方法,比如:讀取配置文件、獲取元素、加載用例等等

import re
import os

import logging
import logging.config
CON_LOG=os.path.abspath((os.path.dirname((os.path.dirname(__file__))))+'/config/log.conf')
logging.config.fileConfig(CON_LOG)
logging=logging.getLogger()

import configparser

from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import TimeoutException
import time
from datetime import datetime

import warnings
warnings.simplefilter("ignore", ResourceWarning)

import subprocess

class ReadTxt():
    def __init__(self,file_path=None):
        if file_path==None:
            self.file_path=os.path.abspath((os.path.dirname(os.path.dirname(__file__)))+'/config/app_info.txt')
        else:
            self.file_path=file_path
    def get_appinfo(self,value): #讀取app的配置文件app_info.txt
        logging.info("讀取app_info.txt文件")
        with open(self.file_path) as f:
            lines=f.readlines()
        for line in lines:
            key_value=re.split('=',line)
            if key_value[0]==value:
                return key_value[1].replace("\n","")
            else:
                continue

class ReadIni():  #讀取ini文件的類
    def __init__(self,file_path=None):
        if file_path==None:
            self.file_path=os.path.abspath((os.path.dirname(os.path.dirname(__file__)))+'/config/page.ini')
        else:
            self.file_path=file_path
        self.data=self.read_ini()
    def read_ini(self):
        read_ini=configparser.ConfigParser()
        read_ini.read(self.file_path,encoding="utf-8-sig")
        return read_ini

    def get_value(self,session,key):
        logging.info("get value: ["+session +"] - [" + key +"] from "+ self.file_path )
        try:
            value=self.data.get(session,key)
        except:
            logging.error("獲取元素出現了錯誤,請檢查傳入的session和key是否正確:session: "+session+"  key:"+key)
            value=None
        return value

read_ini=ReadIni()  #實例化一個ReadIni類,在GetByLocal類中使用
#定義一個獲取元素的類
class GetByLoacl():
    def __init__(self,driver):
        self.driver=driver

    def getScreenShot(self,text): #封裝截圖函數
        logging.info("開始截圖")
        dt=datetime.now()
        now_time=dt.strftime("%Y_%m_%d_%H_%M_%S")
        image_file=os.path.dirname(os.path.dirname(__file__))+"\\Result\\Screenshots\\%s_%s.png" %(now_time,text)
        logging.info(image_file)
        self.driver.get_screenshot_as_file(image_file)
    def no_find_element(self,section,key,value):
        text="["+section+"]_["+key+"]_"+value+"沒有找到"
        logging.error(text)
        text1=section+"_"+key+"_沒有找到元素"
        self.getScreenShot(text1)

    def get_element(self,session,key,wait=10):
        local=read_ini.get_value(session,key)
        if local is None:
            logging.error("沒有找到元素")
            self.driver.get_screenshot_as_file("1.png")
            return None
        by=local.split('>')[0]
        element_value=local.split('>')[1]

        if by=='id':
            #使用id定位元素
            try:
                element=WebDriverWait(self.driver,wait).until(lambda x:x.find_element_by_id(element_value))
            except TimeoutException:
                self.no_find_element(session,key,element_value)
                return None
            else:
                return element
        elif by=='xpath':  #使用xpath定位
            #self.getScreenShot("測試一下封裝的截圖方法")
            #self.no_find_element(session,key,element_value)
            try:
                element=WebDriverWait(self.driver,wait).until(lambda x:x.find_element_by_xpath(element_value))
            except TimeoutException:
                self.no_find_element(session, key, element_value)
                return None
            else:
                return element
        elif by=='ids':
            local_num=local.split('>')[2]
            try:
                local_num=int(local_num)
                element=WebDriverWait(self.driver,wait).until(lambda x:x.find_elements_by_id(element_value)[local_num])
            except TimeoutException:
                self.no_find_element(session, key, element_value)
                return None
            else:
                return element
        else:
            logging.error("沒有找到這個查找元素的方法,請檢查get_by_local和page_ini文件,是否是配置文件寫錯,或者是沒有封裝該方法")



def get_files(path):
    #該方法是用來加載用例的,返回用例的列表
    logging.info("現在開始加載用例")
    logging.info('get_files '+path)
    f1=[]
    f_list=os.listdir(path)
    for i in f_list:
        fulldirfile=os.path.join(path,i)
        if os.path.isdir(fulldirfile):  #如果全路徑是文件夾,跳過繼續處理
            continue
        else:
            f1.append(path+"\\"+i)
    text=str(len(f1)) + " cases in the path "+path
    logging.info(text)
    return f1
    #print(f1)


#封裝一個函數,獲取測試報告的路徑,並且要加上時間戳
PRJPATH=os.getcwd()
def get_new_file(path=None):
    now=time.strftime("%Y-%m-%d-%H_%M_%S",time.localtime(time.time()))
    filename=PRJPATH
    if path is None:
        path='/report/'
    else:
        path='/'+path+'/'

    filename=filename+path+'text report '+now +'.html'

    logging.info("get_new_file "+filename)

    if not os.path.exists(os.path.dirname(filename)):
        os.makedirs(os.path.dirname(filename))
    return filename


def get_new_dir(case):  #這個是用來創建存放app日誌的文件的
    now=time.strftime("%Y-%m-%d-%H_%M_%S",time.localtime(time.time()))
    filename=PRJPATH
    filename=filename+'/logs/'+os.path.basename(case)+'/'+now
    logging.info("get_new_dir "+filename)

    os.makedirs(filename)
    logging.info('make a new dir')
    return filename

def start_logcat(filename):
    filename=filename+'/logcat.log'
    logging.info("start logcat "+filename)
    logcat_file=open(filename,'w')
    ip=ReadTxt().get_appinfo('udid') #獲取設備的udid
    logcmd="adb logcat -v threadtime"
    logcmd=logcmd.replace('adb','adb -s '+ip)
    proc=subprocess.Popen(logcmd,stdout=logcat_file,stderr=subprocess.PIPE)
    return proc,filename


def end_logcat(proc):
    logging.info("end logcat")
    proc.terminate()

def read_case(path):
    lines=open(path,encoding='utf-8',errors='ignore').readlines()
    steps=[]

    for l in lines:
        ll=l.strip()  #處理空格或者換行符
        if len(ll)<2:
            continue
        if ll.startswith('#'):
            continue
        logging.info(ll)
        d=ll.split('|')
        steps.append(tuple(d))
    return steps




if __name__=="__main__":
    # logging.info("這個是測試一下日誌配置文件")
    # path=os.path.dirname(os.path.dirname(__file__))
    # print(path)
    # logging.info("這是測試一下讀取ini文件")
    # read_ini=ReadIni()
    # result=read_ini.get_value('shouye','fabu')
    # print(result)
    dt = datetime.now()
    now_time = dt.strftime("%Y_%m_%d_%H_%M_%S")
    print(dt)
    print(now_time)

    get_files("D:\\Project_python\\Appium\\auto_case\\case")
    get_new_file()
    PRJPATH = os.getcwd()
    print(PRJPATH)

    case="D:\\Project_python\\Appium\\auto_case\\case\\test_fabu.py"
    case_name = case.split('\\')[-1].split('.')[0]
    print(case_name)

 

3.配置文件

配置文件config下有三個文件,一個是app_info.txt,存放的是app啓動信息;一個是log.conf,存放的是日誌的配置文件,一個啊page.ini,存放的是頁面元素的信息,直接把頁面元素放到這裏就可以了。

app_info.txt

platformName=Android
platformVersion=9.0
deviceName=HONOR9X
udid=GDB6R19718023173
appPackage=com.ss.android.article.news
appActivity=com.ss.android.article.news.activity.MainActivity
server=http://localhost:4723/wd/hub

log.conf

[loggers]
keys=root,infoLogger

[logger_root]
level=DEBUG
handlers=consoleHandler,fileHandler

[logger_infoLogger]
handlers=consoleHandler,fileHandler
qualname=infoLogger
propagate=0

[handlers]
keys=consoleHandler,fileHandler

[handler_consoleHandler]
class=StreamHandler
level=INFO
formatter=form02
args=(sys.stdout,)

[handler_fileHandler]
class=FileHandler
level=INFO
formatter=form01
args=('../Result/Logs/runlog.log', 'a+')

[formatters]
keys=form01,form02

[formatter_form01]
format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s

[formatter_form02]
format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s

page.ini

[shouye]
fabu=xpath>//*[@text="發佈"]
xiguanshipin=xpath>//*[@text="西瓜視頻"]
[fabupage]
caogaoxiang=xpath>//*[@text="草稿箱"]

4.封裝base_driver

base_driver.py中啓動app,其appium的參數是從config下的app_info.txt中讀取的

from appium import webdriver
import time
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support.ui import WebDriverWait
from public.file import ReadTxt
readtxt=ReadTxt()  #導入讀取txt的類
import logging

def android_driver():
    logging.info("開始啓動Android APP")
    desired_caps={}
    desired_caps['platformName']=readtxt.get_appinfo("platformName")
    desired_caps['platformVersion']=readtxt.get_appinfo("platformVersion")
    desired_caps['deviceName']=readtxt.get_appinfo("deviceName")
    desired_caps['udid']=readtxt.get_appinfo("udid")
    desired_caps['appPackage']=readtxt.get_appinfo("appPackage")
    desired_caps['appActivity']=readtxt.get_appinfo("appActivity")
    desired_caps['noReset']=True
    driver=webdriver.Remote(readtxt.get_appinfo("server"),desired_caps)
    time.sleep(10)
    return driver

if __name__=='__main__':
    driver=android_driver()
    time.sleep(10)
    try:
        print("開始定位元素")
        element=WebDriverWait(driver,10).until(lambda x:x.find_element_by_xpath('//*[@text="發佈1"]'))
    except TimeoutException:
        logging.error("這個出現了錯誤,超時了")
    else:
        logging.info("開始點擊操作")
        element.click()

5.封裝po.py

po.py中,存放的是元素的操作邏輯


from base.base_driver import android_driver
import logging
from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import TimeoutException
import sys
import time
from public.file import GetByLoacl

class pageobject():
    def __init__(self):
        self.driver=android_driver()
        self.get_element=GetByLoacl(self.driver)

    def go_home(self):
        self.driver.start_activity("com.ss.android.article.news","com.ss.android.article.news.activity.MainActivity")
        time.sleep(5)

    def click_fabu(self):
        logging.info("點擊發布按鈕")
        element=self.get_element.get_element('shouye','fabu')
        element.click()
 
    def check_clici_fabu(self):
        logging.info("現在開始檢查是否有草稿箱元素")
        element=self.get_element.get_element("fabupage","caogaoxiang")
        if element is None:
            return False
        else:
            return True

if __name__=="__main__":
    po=pageobject()
    po.click_fabu()
    result=po.check_clici_fabu()
    if result==True:
        logging.info("執行成功")
    else:
        logging.error("執行失敗")

6.封裝case.py

case.py是執行用例的入口

import sys
import unittest
import HTMLTestRunner
from PO.po import pageobject
import logging

import warnings
warnings.simplefilter("ignore",ResourceWarning)

class CaseTest(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.pog=pageobject()
        logging.info("setup class")
    def setUp(self):
        logging.info("This is setup")


    def test_001(self):
        logging.info("開始測試點擊發布的功能")
        self.pog.click_fabu()
        self.assertTrue(self.pog.check_clici_fabu())
    def test_002(self):

        self.pog.go_home()
        logging.info("在此測試一下發布功能")
        self.pog.click_fabu()
        self.assertTrue(self.pog.check_clici_fabu())


    def tearDown(self):
        logging.info("tearDown")
    @classmethod
    def tearDownClass(cls):
        logging.info("This is teardownclass")


def get_suit():
    logging.info("開始執行用例")

    suite=unittest.TestSuite()
    suite.addTest(CaseTest("test_001"))
    suite.addTest(CaseTest("test_002"))

    html_file="D:\\Project_python\\Appium\\Result\\Report\\report.html"
    fp=open(html_file,'wb')
    HTMLTestRunner.HTMLTestRunner(stream=fp).run(suite)

if __name__=="__main__":
    logging.info("開始執行")
    get_suit()

詳情請點擊 https://study.163.com/course/introduction/1209833813.htm 查看

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章