po+dd的ui自動化demo

關(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即可伊诵。
podriver比較直接只是對對象的解析(嘗試用遞歸寫的時候单绑,有點問題,后面排查曹宴,先直接手寫)

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()
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末活翩,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子翻伺,更是在濱河造成了極大的恐慌材泄,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件吨岭,死亡現(xiàn)場離奇詭異拉宗,居然都是意外死亡,警方通過查閱死者的電腦和手機辣辫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進店門旦事,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人急灭,你說我怎么就攤上這事姐浮。” “怎么了化戳?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵单料,是天一觀的道長。 經(jīng)常有香客問我点楼,道長扫尖,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任掠廓,我火速辦了婚禮换怖,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蟀瞧。我一直安慰自己沉颂,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布悦污。 她就那樣靜靜地躺著铸屉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪切端。 梳的紋絲不亂的頭發(fā)上彻坛,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天,我揣著相機與錄音踏枣,去河邊找鬼昌屉。 笑死,一個胖子當(dāng)著我的面吹牛茵瀑,可吹牛的內(nèi)容都是我干的间驮。 我是一名探鬼主播,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼马昨,長吁一口氣:“原來是場噩夢啊……” “哼竞帽!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起鸿捧,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤抢呆,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后笛谦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體抱虐,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年饥脑,在試婚紗的時候發(fā)現(xiàn)自己被綠了恳邀。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,650評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡灶轰,死狀恐怖谣沸,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情笋颤,我是刑警寧澤乳附,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布内地,位于F島的核電站,受9級特大地震影響赋除,放射性物質(zhì)發(fā)生泄漏阱缓。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一举农、第九天 我趴在偏房一處隱蔽的房頂上張望荆针。 院中可真熱鬧,春花似錦颁糟、人聲如沸航背。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽玖媚。三九已至,卻和暖如春婚脱,著一層夾襖步出監(jiān)牢的瞬間最盅,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工起惕, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留涡贱,地道東北人。 一個月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓惹想,卻偏偏與公主長得像问词,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子嘀粱,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,527評論 2 349

推薦閱讀更多精彩內(nèi)容