簡介
江湖有言:”代碼寫死一時爽,框架重構(gòu)火葬場“,更有人戲言:”代碼動態(tài)一時爽,一直動態(tài)一直爽“,雖然聽起來有點聳人聽聞,但也沒有想象中的那么嚴(yán)重,我們在開發(fā)寫代碼的時候留心和注意就可以了。
為了重構(gòu)時,少掉些頭發(fā),在開發(fā)的時候就得注意了。
?
寫死代碼后,有變動后出現(xiàn)bug后我們的反應(yīng)
大佬和菜鳥對遺留寫死代碼的反應(yīng)
最后和宏哥一起膜拜一下能夠重構(gòu)寫死代碼的大牛
是不是有宏哥的風(fēng)范啊
閑話少說,進入今天的主題:PageObject+unittest。
問題思考
前面我們都是基于線性模型來編寫測試腳本,而且元素定位方式和屬性值都是寫死的。在業(yè)務(wù)場景簡單的情況下這樣寫無可厚非,但是一旦遇到產(chǎn)品需求變更,業(yè)務(wù)邏輯比較復(fù)雜需要維護的時候就非常麻煩了,那么該如何應(yīng)對這種情況呢?
場景案例
結(jié)合前面我們所學(xué),測試考研幫 App登錄場景,按照線性模型來構(gòu)造出腳本如下: 考研幫登錄測試場景
kyb_login.py
代碼實現(xiàn)
參考代碼
# coding=utf-
8
#
1
.先設(shè)置編碼,utf-
8可支持中英文,如上,一般放在第一行
#
2
.注釋:包括記錄創(chuàng)建時間,創(chuàng)建人,項目名稱。
'''
Created on
2019
-
8
-
16
@author: 北京
-宏哥 QQ交流群:
707699217
Project:學(xué)習(xí)和使用appium自動化測試
-代碼寫死一時爽,框架重構(gòu)火葬場 - PageObject+
unittest
'''
#
3
.導(dǎo)入模塊
from
appium import webdriver
import yaml
from
selenium.common.exceptions import NoSuchElementException
import logging
import logging.config
CON_LOG
=
'
../log/log.conf
'
logging.config.fileConfig(CON_LOG)
logging
=
logging.getLogger()
stream
=open(
'
../yaml/desired_caps.yaml
'
,
'
r
'
)
data
=
yaml.load(stream)
desired_caps
=
{}
desired_caps[
'
platformName
'
]=data[
'
platformName
'
]
desired_caps[
'
platformVersion
'
]=data[
'
platformVersion
'
]
desired_caps[
'
deviceName
'
]=data[
'
deviceName
'
]
desired_caps[
'
app
'
]=data[
'
app
'
]
desired_caps[
'
noReset
'
]=data[
'
noReset
'
]
desired_caps[
'
unicodeKeyboard
'
]=data[
'
unicodeKeyboard
'
]
desired_caps[
'
resetKeyboard
'
]=data[
'
resetKeyboard
'
]
desired_caps[
'
appPackage
'
]=data[
'
appPackage
'
]
desired_caps[
'
appActivity
'
]=data[
'
appActivity
'
]
driver
= webdriver.Remote(
'
http://
'
+str(data[
'
ip
'
])+
'
:
'
+str(data[
'
port
'
])+
'
/wd/hub
'
, desired_caps)
def check_updateBtn():
logging.info(
"
check_updateBtn
"
)
try
:
element
= driver.find_element_by_id(
'
android:id/button2
'
)
except NoSuchElementException:
logging.info(
'
update element is not found!
'
)
else
:
element.click()
def check_skipBtn():
logging.info(
"
check_skipBtn
"
)
try
:
element
= driver.find_element_by_id(
'
com.tal.kaoyan:id/tv_skip
'
)
except NoSuchElementException:
logging.info(
'
skipBtn element is not found!
'
)
else
:
element.click()
check_updateBtn()
check_skipBtn()
logging.info(
'
start login...
'
)
driver.find_element_by_id(
'
com.tal.kaoyan:id/login_email_edittext
'
).send_keys(
'
自學(xué)網(wǎng)2018
'
)
driver.find_element_by_id(
'
com.tal.kaoyan:id/login_password_edittext
'
).send_keys(
'
zxw2018
'
)
driver.find_element_by_id(
'
com.tal.kaoyan:id/login_login_btn
'
).click()
logging.info(
'
login finished
'
)
案例分析
上面的腳本看似都比較完善,有了 log采集,參數(shù)配置、啟動時頁面元素自動檢測。但是也存在一些不足之處:
- 公共模塊和業(yè)務(wù)模塊混合在一起顯得代碼冗余等
- 測試場景單一(如果要實現(xiàn)如下測試場景該怎么辦?)
- 元素定位屬性和代碼混雜在一起
以上這些都是需要優(yōu)化的地方。
|
測試場景 |
操作步驟 |
預(yù)期結(jié)果 |
|
多賬號登錄 |
不同的用戶名密碼來進行登錄 |
能夠正常登錄 |
|
異常登錄 |
用戶名或者密碼錯誤、或者為空進行登錄, |
登錄失敗,同時界面要給出相應(yīng)的提示 |
|
注冊 |
點擊注冊,然后進行注冊信息填寫 |
能夠注冊成功 |
重構(gòu)優(yōu)化思路
- 將一些公共的內(nèi)容(如: check_updateBtn,check_skipBtn,capability)抽離出來。
- 元素定位方法和元素屬性值與業(yè)務(wù)代碼分離
- 登錄功能模塊封裝為一個獨立的模塊
- 使用 unittest進行用例綜合管理
Page Object
Page Object是Selenium自動化測試項目開發(fā)實踐的最佳設(shè)計模式之一,通過對界面元素的封裝減少冗余代碼,同時在后期維護中,若元素定位發(fā)生變化,只需要調(diào)整頁面元素封裝的代碼,提高測試用例的可維護性。
腳本實現(xiàn)
封裝 App啟動配置信息
desired_caps.py
代碼實現(xiàn)
參考代碼
# coding=utf-
8
#
1
.先設(shè)置編碼,utf-
8可支持中英文,如上,一般放在第一行
#
2
.注釋:包括記錄創(chuàng)建時間,創(chuàng)建人,項目名稱。
'''
Created on
2019
-
8
-
16
@author: 北京
-宏哥 QQ交流群:
707699217
Project:學(xué)習(xí)和使用appium自動化測試
-代碼寫死一時爽,框架重構(gòu)火葬場 - PageObject+
unittest
'''
#
3
.導(dǎo)入模塊
from
appium import webdriver
import yaml
import logging
import logging.config
CON_LOG
=
'
../log/log.conf
'
logging.config.fileConfig(CON_LOG)
logging
=
logging.getLogger()
def appium_desired():
file
= open(
'
../yaml/desired_caps.yaml
'
,
'
r
'
)
data
=
yaml.load(file)
desired_caps
=
{}
desired_caps[
'
platformName
'
]=data[
'
platformName
'
]
desired_caps[
'
platformVersion
'
]=data[
'
platformVersion
'
]
desired_caps[
'
deviceName
'
]=data[
'
deviceName
'
]
desired_caps[
'
app
'
]=data[
'
app
'
]
desired_caps[
'
appPackage
'
]=data[
'
appPackage
'
]
desired_caps[
'
appActivity
'
]=data[
'
appActivity
'
]
desired_caps[
'
noReset
'
]=data[
'
noReset
'
]
desired_caps[
'
unicodeKeyboard
'
]=data[
'
unicodeKeyboard
'
]
desired_caps[
'
resetKeyboard
'
]=data[
'
resetKeyboard
'
]
logging.info(
'
start app...
'
)
driver
=webdriver.Remote(
'
http://
'
+str(data[
'
ip
'
])+
'
:
'
+str(data[
'
port
'
])+
'
/wd/hub
'
,desired_caps)
driver.implicitly_wait(
8
)
return
driver
if
__name__ ==
'
__main__
'
:
appium_desired()
記得在原來的 yaml配置表desired_caps.yaml補充如下內(nèi)容:
unicodeKeyboard:?True?
resetKeyboard:?True
封裝基類:?
baseView.py
代碼實現(xiàn)
參考代碼
# coding=utf-
8
#
1
.先設(shè)置編碼,utf-
8可支持中英文,如上,一般放在第一行
#
2
.注釋:包括記錄創(chuàng)建時間,創(chuàng)建人,項目名稱。
'''
Created on
2019
-
8
-
16
@author: 北京
-宏哥 QQ交流群:
707699217
Project:學(xué)習(xí)和使用appium自動化測試
-代碼寫死一時爽,框架重構(gòu)火葬場 - PageObject+
unittest
'''
#
3
.定義類
class
BaseView(
object
):
def __init__(self,driver):
self.driver
=
driver
def find_element(self,
*
loc):
return
self.driver.find_element(*loc)
封裝通用公共類
common_fun.py
代碼實現(xiàn)
參考代碼
# coding=utf-
8
#
1
.先設(shè)置編碼,utf-
8可支持中英文,如上,一般放在第一行
#
2
.注釋:包括記錄創(chuàng)建時間,創(chuàng)建人,項目名稱。
'''
Created on
2019
-
8
-
16
@author: 北京
-宏哥 QQ交流群:
707699217
Project:學(xué)習(xí)和使用appium自動化測試
-代碼寫死一時爽,框架重構(gòu)火葬場 - PageObject+
unittest
'''
#
3
.導(dǎo)入模塊
from
page_object.baseView import BaseView
from
page_object.desired_caps import appium_desired
from
selenium.common.exceptions import NoSuchElementException
import logging
from
selenium.webdriver.common.by import By
class
Common(BaseView):
cancelBtn
=(By.ID,
'
android:id/button2
'
)
skipBtn
=(By.ID,
'
com.tal.kaoyan:id/tv_skip
'
)
def check_cancelBtn(self):
logging.info(
'
==========check_cancelBtn=========
'
)
try
:
cancelBtn
= self.driver.find_element(*
self.cancelBtn)
except NoSuchElementException:
logging.info(
'
no cancelBtn
'
)
else
:
cancelBtn.click()
def check_skipBtn(self):
logging.info(
'
=========check skipBtn=============
'
)
try
:
skipBtn
= self.driver.find_element(*
self.skipBtn)
except NoSuchElementException:
logging.info(
'
no skipBtn
'
)
else
:
skipBtn.click()
if
__name__ ==
'
__main__
'
:
driver
=
appium_desired()
com
=
Common(driver)
com.check_cancelBtn()
com.check_skipBtn()
封裝登錄操作?
loginView.py
代碼實現(xiàn)
參考代碼
# coding=utf-
8
#
1
.先設(shè)置編碼,utf-
8可支持中英文,如上,一般放在第一行
#
2
.注釋:包括記錄創(chuàng)建時間,創(chuàng)建人,項目名稱。
'''
Created on
2019
-
8
-
16
@author: 北京
-宏哥 QQ交流群:
707699217
Project:學(xué)習(xí)和使用appium自動化測試
-代碼寫死一時爽,框架重構(gòu)火葬場 - PageObject+
unittest
'''
#
3
.導(dǎo)入模塊
import logging
from
page_object.common_fun import Common
from
page_object.desired_caps import appium_desired
from
selenium.webdriver.common.by import By
class
LoginView(Common):
username_type
=(By.ID,
'
com.tal.kaoyan:id/login_email_edittext
'
)
password_type
=(By.ID,
'
com.tal.kaoyan:id/login_password_edittext
'
)
loginBtn
=(By.ID,
'
com.tal.kaoyan:id/login_login_btn
'
)
def login_action(self,username,password):
self.check_cancelBtn()
self.check_skipBtn()
logging.info(
'
============login_action==============
'
)
logging.info(
'
username is:%s
'
%
username)
self.driver.find_element(
*
self.username_type).send_keys(username)
logging.info(
'
password is:%s
'
%
password)
self.driver.find_element(
*
self.password_type).send_keys(password)
logging.info(
'
click loginBtn
'
)
self.driver.find_element(
*
self.loginBtn).click()
logging.info(
'
login finished!
'
)
if
__name__ ==
'
__main__
'
:
driver
=
appium_desired()
l
=
LoginView(driver)
l.login_action(
'
北京-宏哥-2019
'
,
'
bjhg2019
'
)
unittest用例封裝
測試場景
使用如下賬號進行分別登錄測試
|
用戶名 |
密碼 |
|
自學(xué)網(wǎng)2018 |
zxw2018 |
|
自學(xué)網(wǎng)2017 |
zxw2017 |
|
666 |
222 |
Tips必備基礎(chǔ)知識: Selenium自動化第六章-unittest單元測試框架
1.封裝用例啟動結(jié)束時的配置:?
myunit.py
代碼實現(xiàn)
參考代碼
# coding=utf-
8
#
1
.先設(shè)置編碼,utf-
8可支持中英文,如上,一般放在第一行
#
2
.注釋:包括記錄創(chuàng)建時間,創(chuàng)建人,項目名稱。
'''
Created on
2019
-
8
-
16
@author: 北京
-宏哥 QQ交流群:
707699217
Project:學(xué)習(xí)和使用appium自動化測試
-代碼寫死一時爽,框架重構(gòu)火葬場 - PageObject+
unittest
'''
#
3
.導(dǎo)入模塊
import unittest
from
page_object.desired_caps import appium_desired
import logging
from
time import sleep
class
StartEnd(unittest.TestCase):
def setUp(self):
logging.info(
'
=====setUp====
'
)
self.driver
=
appium_desired()
def tearDown(self):
logging.info(
'
====tearDown====
'
)
sleep(
5
)
self.driver.close_app()
2.用例封裝?
test_login.py
代碼實現(xiàn)
參考代碼
# coding=utf-
8
#
1
.先設(shè)置編碼,utf-
8可支持中英文,如上,一般放在第一行
#
2
.注釋:包括記錄創(chuàng)建時間,創(chuàng)建人,項目名稱。
'''
Created on
2019
-
8
-
16
@author: 北京
-宏哥 QQ交流群:
707699217
Project:學(xué)習(xí)和使用appium自動化測試
-代碼寫死一時爽,框架重構(gòu)火葬場 - PageObject+
unittest
'''
#
3
.導(dǎo)入模塊
from
unittest.myunit import StartEnd
from
page_object.loginView import LoginView
import unittest
import logging
class
TestLogin(StartEnd):
def test_login_bjhg2019(self):
logging.info(
'
======test_login_bjhg-2019=====
'
)
l
=
LoginView(self.driver)
l.login_action(
'
北京宏哥-2018
'
,
'
bjhg-2019
'
)
def test_login_bjhg2018(self):
logging.info(
'
======test_login_bjhg-2018=====
'
)
l
=
LoginView(self.driver)
l.login_action(
'
北京宏哥-2018
'
,
'
bjhg-2018
'
)
def test_login_error(self):
logging.info(
'
======test_login_error=====
'
)
l
=
LoginView(self.driver)
l.login_action(
'
6666
'
,
'
222
'
)
if
__name__ ==
'
__main__
'
:
unittest.main()
小結(jié)
1.代碼運行流程圖
?
?2.宏哥箴言:
代碼寫死一時爽,框架重構(gòu)火葬場。此處功能將來必改,不要寫死!
?3.最后大家要且行且珍惜,出來混遲早晚要還的。 ( ^__^ ) 嘻嘻……
?
您的肯定就是我進步的動力。 如果你感覺還不錯,就請鼓勵一下吧!記得點波? 推薦 ?哦!!! (點擊右邊的小球即可!( ^__^ ) 嘻嘻……)
?
.
? ? ? ?個人公眾號? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 微信群 ?(微信群已滿100,可以加宏哥的微信拉你進群,請備注:進群)? ? ? ? ??
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元

