我們在做自動化的時候應該都聽過PO模型,那么什么是PO模型呢?PO模型在自動化中的作用是什么呢?
PO模型
PO其實就是:、Page Object Model,也稱作為POM模型,PO其實是一種設計模式,已經在自動化測試中流行起來,以增強測試維護並減少代碼重復。頁面對象是面向對象的類,用作頁面的接口和被測設備。 然后,只要測試需要與該頁面的UI進行交互,這些測試便會使用該頁面對象類的方法,其好處在於,如果頁面的UI發生了更改,則無需更改測試本身,只需更改其中的代碼即可。頁面對象需要更改。 隨后,所有支持該新UI的更改都位於一個位置。其實說到低就是一句話:把每一個頁面當作一個類,把頁面上的元素信息和代碼操作分離開,然后方面后面我們進行管理代碼和元素內容
PO分層
PO分層也就是對我們自動化代碼進行分層具體可以分為以下基層
1、基礎層:封裝一些定位方法,點擊,輸入,滑動等操作
2、公用層:獲取元素方法,操作元素方法,獲取CMD信息等方法
3、業務層:頁面元素信息。
4、邏輯層:一些功能,比如登錄,注冊。
5、數據層:測試信息存放地方
emmm,這里是安靜這邊自身的了解,當然可能每個人對PO分層的理解不同,可能大佬們分的比我這里更加詳細。(一起分享,共同學習)
這里安靜簡單拿項目來進行實際介紹下PO內容
首先我們先看以前如何編寫測試用例的
# coding:utf-8 from appium import webdriver import time import unittest class login(unittest.TestCase): def setUp(self): desired_caps = { 'platformName': 'Android', # 測試版本 'deviceName': 'emulator-5554', # 設備名 'platformVersion': '5.1.1', # 系統版本 'appPackage': 'com.taobao.taobao', #apk的包名 'appActivity': 'com.ali.user.mobile.login.ui.UserLoginActivity', # apk的launcherActivity 'noReset':True , # 清除緩存 } self.driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps) def tearDown(self): self.driver.quit() def test01(self): ''' 賬號密碼錯誤 ''' self.driver.implicitly_wait(40) self.driver.find_element_by_id("com.eboss2.sbs:id/tv_username").send_keys("22222") time.sleep(2) self.driver.find_element_by_id("com.eboss2.sbs:id/tv_password").send_keys("33333333") time.sleep(2) self.driver.find_element_by_id("com.eboss2.sbs:id/btn_login").click() time.sleep(5) x = self.driver.find_element_by_id("com.eboss2.sbs:id/shopName_TextView").text print(x) self.assertEqual(x,'請輸入正確的手機號') if __name__ == '__main__': unittest.main()
相信絕大部分的同學,第一次寫代碼的時候都會這樣寫測試用例
PO模型設計框架
肯定有人會問?什么是自動化框架?自動化框架有什么好處呢?這個地方我們先不說答案,我們后面寫
首先把安靜這邊設計的框架先整體列出來,一個個為大家分析
appium_python # 目標工程 - case # 用例存放 test_login.py # 編寫用例 - common # 公用方法 appium_start.py # 啟動appium Base.py # 封裝基礎內容 dos_cmd.py # cmd執行 HTmlTestRunner.py # 報告文件 logger.py # 日志 read_yaml.py # 讀取yaml文件 - config # 頁面元素存放 appium.py # login頁面存放 - function # 功能點 login.py # 登錄邏輯 - logs # 日志存放內容 - pages # 獲取頁面元素信息 login_page.py # 獲取登錄元素信息 -report # 報告存放地方 runTest.py # 主函數
我們這里需要這么多內容,才能完成上面的簡單的操作。
config目錄
這里我們主要存放一些頁面元素信息,前面也寫了兩種方法進行封裝頁面元素
# appium.yaml
LoginPage: dec: 登錄 locators: - name: 用戶名 type: id value: com.taobao.taobao:id/aliuser_login_mobile_et - name: 密碼 type: android value: resourceId("com.taobao.taobao:id/aliuser_register_sms_code_et") - name: 登錄按鈕 type: className value: android.widget.Button
想象下,元素信息有了,我們是不是需要進行讀取元素信息。讀取元素信息的時候,是不是又需要通過PO模型的方法,把每個頁面的元素都列舉出來
common目錄
common目錄中包含一些公用的部分,比如讀取yaml方法,執行cmd內容,appium中常用的方法等操作
# read_yaml.py import yaml import os class GetYaml(): def __init__(self,file_path): # 判斷文件是否存在 if os.path.exists(file_path): self.file_path = file_path else: print('沒有找到%s文件路徑'%file_path) self.data = self.read_yaml() def read_yaml(self): with open(self.file_path,'r',encoding='utf-8')as f: p = f.read() return p def get_data(self,key=None): result = yaml.load(self.data,Loader=yaml.FullLoader) if key == None: return result else: return result.get(key) if __name__ == '__main__': read_yaml = GetYaml('E:/appium_python/config/appium.yaml') xx = read_yaml.get_data('LoginPage') print(xx['locators'])
pages目錄
這里我們把每個類中都代表一個頁面,獲取頁面上的所有信息
# coding:utf-8 from common.Base import BaseApp import os from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from common.loger import Logger path = os.path.dirname(os.path.realpath(__file__)) yaml_path = os.path.join(os.path.join(os.path.dirname(path),'config'),'appium.yaml') class Login_element: def __init__(self,driver): self.log = Logger('element.py') self.driver = driver self.get_element = BaseApp(self.driver) def user_element(self): ''' 獲取用戶名元素''' self.log.info('正在獲取用戶名元素信息---------------------------------------') element = self.get_element.get_element(yaml_path,'LoginPage')['locators'][0] self.log.info('用戶名元素信息為:%s'%element) return element def password_element(self): ''' 獲取密碼元素''' self.log.info('正在獲取用戶名元素信息-------------------------------------') element = self.get_element.get_element(yaml_path,'LoginPage')['locators'][1] self.log.info('密碼元素信息為:%s'%element) return element def login_boot(self): ''' 獲取登錄按鈕元素''' self.log.info('正在獲取用戶名元素信息-------------------------------------') element = self.get_element.get_element(yaml_path,'LoginPage')['locators'][2] self.log.info('登錄按鈕元素信息為:%s'%element) return element def toast(self,message): '''獲取toast信息''' toast_loc = ("xpath", ".//*[contains(@text,'%s')]"%message) element = WebDriverWait(self.driver, 30, 0.1).until(EC.presence_of_element_located(toast_loc)).text return element
頁面用例元素都已經全部獲取出來了,那么我們可以通過封裝一些操作內容,比如說登錄,注冊,然后直接把我們的數據存放進去
function目錄
function目錄表示每個測試點,例如、;登錄和注冊全部都單獨封裝起來,用的時候,可以直接進行調用
# login.py # coding:utf-8 from pages.login_page2 import Login_element class LoginTest: def __init__(self,driver): self.element = Login_element(driver) self.app = self.element.get_element
def login(self,username,password): self.app.send_text(self.element.user_element(),username) self.app.send_text(self.element.password_element(),password) self.app.click(self.element.login_boot())
case目錄
case表示存放測試用例的目錄
# test_login.py from function.login import LoginTest from common.appium_start import start import unittest import threading import time from common.loger import Logger import warnings warnings.simplefilter("ignore", ResourceWarning) class BaseDriver(unittest.TestCase): @classmethod def setUpClass(cls): '''啟動apk''' cls.log = Logger('anjing') cls.log.info('app啟動中') cls.driver = start() cls.log.info('app啟動完成') cls.login = LoginTest(cls.driver) def test01(self): '''賬號密碼錯誤''' self.log.info('用例名稱:賬號密碼錯誤,測試數據:賬號名:11111,密碼:22222,') self.login.login('11111','22222') element= self.login.element.toast('手機號') self.log.info('test01獲取toast信息為:%s'%element) self.assertEqual(element,'請輸入正確的手機號') def test02(self): '''賬號密碼錯誤1''' self.log.info('用例名稱:賬號密碼錯誤1,測試數據:賬號名:222,密碼:33333,') self.login.login('2222','33333') element= self.login.element.toast('手機號') self.log.info('test02獲取toast信息為:%s' %element) self.assertEqual(element,'請輸入正確的手機號') @classmethod def tearDownClass(cls): '''退出APK''' cls.driver.quit() if __name__ == '__main__': t1 = threading.Thread(target=start) t1.start() time.sleep(20) t2 = threading.Thread(target=unittest.main()) t2.start()
logs目錄
logs表示執行用例過程中,存放自己打印的日志地方和appium日志

report目錄
report表示存放測試報告的位置

runTest.py文件
這個主執行文件,用來執行所有的用例,生成測試報告,發送郵件。
# coding:utf-8 import unittest from common import HTMLTestRunner_cn import time import os import smtplib import threading from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart from common.appium_start import start import warnings warnings.simplefilter("ignore", ResourceWarning) # 腳本路徑 path = os.path.dirname(os.path.realpath(__file__)) # case用例 case_path = os.path.join(path,'case') # 執行用例 返回discover def add_case(rule="test*.py"): '''加載所有的測試用例''' # 如果不存在這個case文件夾,就自動創建一個 if not os.path.exists(case_path):os.mkdir(case_path) # 定義discover方法的參數 discover = unittest.defaultTestLoader.discover(case_path, pattern=rule, top_level_dir=None) return discover # 執行報告 def run_case(discover): report_path = os.path.join(path,'report') now = time.strftime("%Y-%m-%d-%H-%M-%S") # 最新的報告 report_abspath = os.path.join(report_path, now+"result.html") # 報告的位置 rp=open(report_abspath,"wb") runner=HTMLTestRunner_cn.HTMLTestRunner(rp, title=u"測試報告", description=u"用例的執行情況") runner.run(discover) return report_abspath def sen_mail(file_path): smtpserver = 'smtp.163.com' # 發送郵箱用戶名密碼 user = 'xxxxxx@163.com' password = 'xxxxxx' # 發送郵箱 sender = 'xxxxxx@163.com' # 接收郵箱 receiver ='821006052@qq.com' #導入報告 with open(file_path, "rb") as fp: mail_body = fp.read() msg=MIMEMultipart() body=MIMEText(mail_body,_subtype="html",_charset="utf-8") msg['Subject']=u'自動化測試報告' msg['from']=sender # 發送郵件 msg['to']=receiver # 接手郵件 msg.attach(body) att = MIMEText(mail_body, "base64", "utf-8") # 生成附件 att["Content-Type"] = "application/octet-stream" att["Content-Disposition"] = 'attachment; filename="report.html"' # 生成附件名稱 msg.attach(att) smtp = smtplib.SMTP() smtp.connect(smtpserver) # 連接服務器 smtp.login(user,password) # 登錄服務器 # 發送郵件 split(',')分隔符 smtp.sendmail(sender, receiver.split(','), msg.as_string()) # 關閉 print ("郵件發送") def main(): discover = add_case() # 調用執行 執行用例 file_path = run_case(discover) # 用例生成報告 # sen_mail(file_path) # 發送報告 if __name__=="__main__": # add_case() t1 = threading.Thread(target=start) t1.start() time.sleep(20) t2 = threading.Thread(target=main) t2.start()
整體的PO模型設計都已經理完了,相信大家肯定都會有一種體驗,怎么感覺原始的方法比較簡單,代碼還少,還簡單,但是如果測試用例較多呢?那么是不是覺得這樣方法就很簡單,很已讀。
這里我們在回復前面留下的那個問題? 自動化框架有什么用?
如果自動化框架建立起來了,那么組里一些代碼基礎比較弱的同學,都可以自己進行按照一個模板進行編寫測試用例。如果頁面元素或者UI發生了變化,我們只需要找到對應的page和元素信息進行修改,這樣就可以繼續保持以前的用例了。
那么當中PO模型也占了很大的用途,很清楚的使我們代碼更加簡潔,已讀。也方便維護。
通過上面的內容,相信大家對PO模型有了簡單的了解,也相信大家都有不同的分層,這里可以留言一起進行討論,學習更多方便簡單的方法
