關(guān)于PO
啥是PO
省流量模式
??PO的設(shè)計方式具有很大的靈活性, 但是有一些基本規(guī)則可以使測試代碼具有理想的可維護性.
??PO本身絕不應(yīng)進行判斷或斷言. 判斷和斷言是測試的一部分, 應(yīng)始終在測試的代碼內(nèi), 而不是在PO中. PO用來包含頁面的表示形式, 以及頁面通過方法提供的服務(wù), 但是與PO無關(guān)的測試代碼不應(yīng)包含在其中.
??實例化PO時, 應(yīng)進行一次驗證, 即驗證頁面以及頁面上可能的關(guān)鍵元素是否已正確加載. 在上面的示例中, SignInPage和HomePage的構(gòu)造函數(shù)均檢查預(yù)期的頁面是否可用并準(zhǔn)備接受測試請求.
??PO不一定需要代表整個頁面. PO設(shè)計模式可用于表示頁面上的組件. 如果自動化測試中的頁面包含多個組件, 則每個組件都有單獨的頁面對象, 則可以提高可維護性.
demo的組成
po框架 - poium 蟲師出品品嚣,支持selenium炕倘、appium以及uiautomator2,源碼是很簡潔的建議閱讀。
測試框架 - unittest
用例的文件格式 - YAML 語言教程 - 阮一峰的網(wǎng)絡(luò)日志
實現(xiàn)思路
??整個實現(xiàn)十分簡單翰撑,載入全部用例后罩旋,通過動態(tài)加載將用例運行方法變成一個個test_xxx方法,然后用unittest運行眶诈。
show you the code
github地址
不用翻墻的gitee地址
1涨醋、用例
用例為template.yaml
文件,可配合data.yaml
做數(shù)據(jù)驅(qū)動逝撬。data.yaml
只是提供參數(shù)浴骂。如果template.yaml
不需要參數(shù)則不需要data.yaml
文件。
template由三種特殊的的調(diào)用包括 Driver宪潮、各種page和Assert溯警。
- driver為
webdriver.Chrome()
,可以幫助我們操作瀏覽器 - page為
page
目錄下我們自己自定義的page類 - Assert為
TestCase
的各種斷言
# template.yaml
# self.driver.get("{url}")
- Driver:
get: "{url}"
# self.driver.maximize_window()
- Driver:
maximize_window:
# BaiduIndexPage.search_input.send_keys("{key}")
- BaiduIndexPage:
search_input:
send_keys: "{key}"
# BaiduIndexPage.search_button.click()
- BaiduIndexPage:
search_button:
click:
# self.assertIn("{key}",self.driver.title)
- Assert:
- in
- "{key}"
- $driver.title
data.yaml提供了參數(shù),方便我們對不同參數(shù)的測試狡相。它的格式為對象組成的數(shù)組梯轻。
# data.yaml
- key: 簡書 hoing # 置換 template中 {key}的關(guān)鍵字
url: https://www.baidu.com # 置換 template中 {url}的關(guān)鍵字
- key: github JoeEmp
url: https://www.baidu.com
2、核心代碼
用例參數(shù)化核心代碼尽棕,利用了format進行了參數(shù)替換
class YamlTemplateCases():
...
def gen_step(self, step, data):
if not data:
return step
if isinstance(step, dict):
for k in step.keys():
step[k] = self.gen_step(step[k], data)
return step
elif isinstance(step, list) or isinstance(step, str):
if isinstance(step, str):
step = [step]
for i in range(len(step)):
# 利用format直接替換參數(shù)
step[i] = step[i].format(**data)
return step
else:
return step
用例執(zhí)行核心代碼
還是以動態(tài)加載的方式來調(diào)用方法
Assert
步驟為了和方法調(diào)用做區(qū)分喳挑,直接是數(shù)組解析。Assert
可以調(diào)用上一次的返回結(jié)果或者是driver是某些功能,只需我們使用$result
和$driver
即可伊诵。
po
和driver
比較直接只是對對象的解析(嘗試用遞歸寫的時候单绑,有點問題,后面排查曹宴,先直接手寫)
class YamlCaseRunner():
...
def exec_assert_step(self, test_case_obj: TestCase, func_args, driver=None, result=None):
assert_type, func_args = func_args[0], func_args[1:]
logging.info(assert_type, func_args)
for i in range(len(func_args)):
if func_args[i].startswith('$result.') and result:
func_args[i] = getattr(result, func_args[i][8:])
elif func_args[i].startswith('$driver.') and driver:
func_args[i] = getattr(driver, func_args[i][8:])
if 'equal' == assert_type.lower():
test_case_obj.assertEqual(*func_args)
elif 'in' == assert_type.lower():
test_case_obj.assertIn(*func_args)
else:
logging.warning('%s類型斷言,尚未支持' % assert_type)
def exec_po_step(self, page, ele_dict, imp_module, driver, cap):
# {'BaiduIndexPage': {'search_input': {'send_keys': '{key}'}}},
# {'BaiduIndexPage': {'search_button': {'click': None}}},
page_class = getattr(imp_module, page)
page_obj = page_class(driver, cap)
for ele, fun_dict in ele_dict.items():
ele_obj = getattr(page_obj, ele)
for func, arg in fun_dict.items():
func_obj = getattr(ele_obj, func)
if arg:
func_obj(*arg)
else:
func_obj()
def exec_driver_step(self, driver, func_dict, cap=None):
# {'Driver': {'get': 'https://www.baidu.com'}}
for func_name, args in func_dict.items():
func = getattr(driver, func_name)
if args:
func(*args)
else:
func()
添加test_xxx的核心代碼
還是通過動態(tài)加載來實現(xiàn)將一個個test_xxx搂橙,塞進TestCase里。
def main():
ym = YamlCaseManager()
length = len(ym.all_cases)
q = deque(ym.all_cases)
def func(self):
case_name, case = q.popleft()
YamlCaseRunner(
case,
self.driver,
test_case_obj=self
)
for i in range(length):
setattr(TestALL, 'test_%s' % i, func)
unittest.main()
總結(jié)
- 為啥要dd笛坦,其實是為了普通的測試人員也能參與到自動測試的工作中去份氧,而dd的做法,成本是相對較低的弯屈。當(dāng)然我覺得behave的形式應(yīng)該比yaml或者是其他的文件更貼近測試用例蜗帜。
- 這個小demo其實已經(jīng)能夠勝任一些自動化的工作了。當(dāng)然如果想要更加健壯和穩(wěn)定的話资厉,還需要我們增加一些觀察機制和偵聽機制厅缺,以便我們能處理一些特殊情況。
- 更簡單的調(diào)用宴偿,我們其實還可以對po做進一步封裝如把一個行為直接封裝起來湘捎,但是等價的,這樣也會使的維護page成本增大窄刘。但是如果組件化的封裝的確更有利于回歸窥妇,那的確值得。一切以實際情況為準(zhǔn)娩践。
from poium import Page, Element
class BaiduIndexPage(Page):
search_input_ele = Element(name='wd')
search_button_ele = Element(id_='su')
def search(self,key):
self.search_input_ele.send_keys(key)
self.search_button_ele.click()