軟件測試開發(fā)實戰(zhàn)|記錄寫裝飾器時踩的幾個坑

背景
裝飾器是python里面一個很有用的語法糖( Syntactic Sugar),可以減少大量重復(fù)代碼的編寫首懈。

剛好最近學(xué)習(xí)了app自動化框架的異常處理菱农,存在一定重復(fù)代碼炕淮,準備當作題材缠借,拿來練習(xí)一下裝飾器干毅。

下面記錄一下裝飾器的踩坑之路。

坑 1:Hint: make sure your test modules/packages have valid Python names.
報錯信息
test_market.py:None (test_market.py)
ImportError while importing test module 'D:\project\Hogwarts_11\test_appium\testcase\test_market.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
test_market.py:9: in <module>
from test_appium.page.app import App
..\page\app.py:12: in <module>
from test_appium.page.base_page import BasePage
..\page\base_page.py:16: in <module>
from test_appium.utils.exception import exception_handle
..\utils\exception.py:11: in <module>
from test_appium.page.base_page import BasePage
E ImportError: cannot import name 'BasePage' from 'test_appium.page.base_page' (D:\project\Hogwarts_11\test_appium\page\base_page.py)
原因
exception.py 文件和 base_page.py 文件之間存在相互調(diào)用關(guān)系泼返。

解決方案
把循環(huán)調(diào)用的包引入信息放在函數(shù)內(nèi)硝逢。只要一方的引用信息放在函數(shù)里即可,不必兩邊都放绅喉。

我只在 exception.py 文件里改了渠鸽,base_page.py 保持不變。

exception.py
def exception_handle(func):
def magic(*args, **kwargs):
# 防止循環(huán)調(diào)用報錯
from test_appium.page.base_page import BasePage
# 獲取BasePage實例對象的參數(shù)self柴罐,這樣可以復(fù)用driver
_self: BasePage = args[0]
...
坑 2:IndexError: tuple index out of range
報錯信息
test_search.py:None (test_search.py)
test_search.py:11: in <module>
from test_appium.page.app import App
..\page\app.py:12: in <module>
from test_appium.page.base_page import BasePage
..\page\base_page.py:52: in <module>
class BasePage:
..\page\base_page.py:74: in BasePage
def find(self, locator, key=None):
..\page\base_page.py:50: in exception_handle
return magic()
..\page\base_page.py:24: in magic
_self: BasePage = args[0]
E IndexError: tuple index out of range
原因
第一次寫裝飾器真的很容易犯這個錯徽缚,來看下哪里寫錯了

def decorator(func):
def magic(*args, *kwargs):
_self: BasePage = args[0]
...
return magic(
args, **kwargs)
# 這里的問題!8锿馈凿试!不應(yīng)該返回函數(shù)調(diào)用,要返回函數(shù)名稱K浦ァ:焓 !
return magic()
為什么返回函數(shù)調(diào)用會報這個錯呢国觉?

因為調(diào)用magic()函數(shù)的時候吧恃,沒有傳參進去,但是magic()里面引用了入?yún)⒙榫鳎@時args沒有值痕寓,自然就取不到args[0]了。

解決方案
去掉括弧就好了

def decorator(func):
def magic(*args, *kwargs):
_self: BasePage = args[0]
...
return magic(
args, **kwargs)
# 返回函數(shù)名蝇闭,即函數(shù)本身
return magic
坑 3:異常處理只執(zhí)行了1次呻率,自動化無法繼續(xù)
報錯信息
主要是定位元素過程中出現(xiàn)的各種異常,NoSuchElementException呻引、TimeoutException等常見問題礼仗。

原因
異常處理后,遞歸邏輯寫得不對逻悠。return func()執(zhí)行了func()元践,跳出了異常處理邏輯,所以異常處理只執(zhí)行一次童谒。

正確的寫法是 return magic()单旁。

感覺又是裝飾器小白容易犯的錯誤…emmm…

解決方案
為了直觀,已過濾不重要代碼饥伊,異常處理邏輯代碼會在文末放出象浑。

def exception_handle(func):
def magic(*args, *kwargs):
_self: BasePage = args[0]
try:
return func(
args, kwargs)
# 彈窗等異常處理邏輯
except Exception as e:
for element in _self._black_list:
elements = _self._driver.find_elements(
element)
if len(elements) > 0:
elements[0].click()
# 異常處理結(jié)束蔫饰,遞歸繼續(xù)查找元素
# 這里之前寫成了return func(
args, *kwargs),所以異常只執(zhí)行一次S洳颉Bㄓ酢!r嚼埂杖剪!
return magic(
args, **kwargs)
raise e
return magic
坑 4:如何復(fù)用driver?
問題
自己剛開始嘗試寫裝飾器的時候外盯,發(fā)現(xiàn)一個問題。

裝飾器內(nèi)需要用到 find_elements翼雀,這時候 driver 哪里來饱苟?還有 BasePage 的私有變量 error_max 和 error_count 怎么獲取到呢?創(chuàng)建一個 BasePage 對象狼渊?然后通過 func 函數(shù)來傳遞 driver 箱熬?

func的driver是私有的,不能外部調(diào)用(事實證明可以emmm…)狈邑。

我嘗試把異常相關(guān)的變量做成公共的城须,沒用,還是無法解決find_elements的調(diào)用問題米苹。

解決方案
思寒的做法是糕伐,在裝飾器里面創(chuàng)建一個self變量,取args[0]蘸嘶,即函數(shù)func的第一個入?yún)elf良瞧。

_self: BasePage = args[0]這一簡單的語句成功解答了我所有的疑問。

類函數(shù)定義里面 self 代表類自身训唱,因此可以獲取 ._driver 屬性褥蚯,從而調(diào)用 find_elements。

坑 5:AttributeError
找到元素后况增,準備點擊的時候報錯

報錯信息
EINFO:root:('id', 'tv_search')
INFO:root:None
INFO:root:('id', 'image_cancel')
INFO:root:('id', 'tv_agree')
INFO:root:('id', 'tv_search')
INFO:root:None

test setup failed
self = <test_appium.testcase.test_search.TestSearch object at 0x0000018946B70940>

def setup(self):
  self.page = App().start().main().goto_search()

test_search.py:16:


self = <test_appium.page.main.MainPage object at 0x0000018946B70780>

def goto_search(self):
  self.find(self._search_locator).click()

E AttributeError: 'NoneType' object has no attribute 'click'

..\page\main.py:20: AttributeError
原因
看了下 find 函數(shù)赞庶,找到元素后,有返回元素本身

@exception_handle
def find(self, locator, key=None):
logging.info(locator)
logging.info(key)
# 定位符支持元組格式和兩個參數(shù)格式
locator = locator if isinstance(locator, tuple) else (locator, key)
WebDriverWait(self._driver, 10).until(expected_conditions.visibility_of_element_located(locator))
element = self._driver.find_element(*locator)
return element
那就是裝飾器寫得不對了

def exception_handle(func):
def magic(*args, *kwargs):
_self: BasePage = args[0]
try:
# 這里只是執(zhí)行了函數(shù)澳骤,但是沒有return
func(
args, **kwargs)
# 彈窗等異常處理邏輯
except Exception as e:
raise e
return magic
解決方案
要在裝飾器里面返回函數(shù)調(diào)用歧强,要不然函數(shù)本身的返回會被裝飾器吃掉。

def exception_handle(func):
def magic(*args, *kwargs):
_self: BasePage = args[0]
try:
# return函數(shù)執(zhí)行結(jié)果
return func(
args, *kwargs)
# 彈窗等異常處理邏輯
except Exception as e:
raise e
return magic
思考:寫裝飾器的時候为肮,各種return看著有點頭暈誊锭。每個函數(shù)里面都可以return,分別代表什么含義呢弥锄?丧靡?蟆沫?
def exception_handle(func):
def magic(
args, *kwargs):
_self: BasePage = args[0]
try:
# 第1處 return:傳遞func()函數(shù)的返回值。如果不寫温治,原有return則失效
return func(
args, kwargs)
# 彈窗等異常處理邏輯
except Exception as e:
for element in _self._black_list:
elements = _self._driver.find_elements(
element)
if len(elements) > 0:
elements[0].click()
# 異常處理結(jié)束饭庞,遞歸繼續(xù)查找元素
# 第2處 return:遞歸調(diào)用裝飾后的函數(shù)。magic()表示新函數(shù)熬荆,func()表示原函數(shù)舟山,不可混淆
return magic(
args, **kwargs)
raise e
# 第3處 return:返回裝飾后的函數(shù),裝飾器語法卤恳。不能返回函數(shù)調(diào)用magic()
return magic
裝飾器完整實現(xiàn)
exception.py
import logging

logging.basicConfig(level=logging.INFO)

def exception_handle(func):
def magic(*args, *kwargs):
# 防止循環(huán)調(diào)用報錯
from test_appium.page.base_page import BasePage
# 獲取BasePage實例對象的參數(shù)self累盗,這樣可以復(fù)用driver
_self: BasePage = args[0]
try:
# logging.info('error count is %s' % _self._error_count)
result = func(
args, kwargs)
_self._error_count = 0
# 返回調(diào)用函數(shù)的執(zhí)行結(jié)果,要不然返回值會被裝飾器吃掉
return result
# 彈窗等異常處理邏輯
except Exception as e:
# 如果超過最大異常處理次數(shù)突琳,則拋出異常
if _self._error_count > _self._error_max:
raise e
_self._error_count += 1
for element in _self._black_list:
# 用find_elements若债,就算找不到元素也不會報錯
elements = _self._driver.find_elements(
element)
logging.info(element)
# 是否找到彈窗
if len(elements) > 0:
# 出現(xiàn)彈窗,點擊掉
elements[0].click()
# 彈窗點掉后拆融,重新查找目標元素
return magic(
args, **kwargs)
# 彈窗也沒有出現(xiàn)蠢琳,則拋出異常
logging.warning("no error is found")
raise e
return magic
學(xué)習(xí)心得
最好先不看思寒的講解,根據(jù)自己的理解寫一遍裝飾器镜豹,這樣學(xué)習(xí)效果最好傲须。

遇到問題嘗試解決,踩過的坑印象深刻。

實在沒有頭緒再參考思寒的解法,那時會有一種豁然開朗的感覺松嘶。

目前就踩到這些坑,如有遺漏菇绵,歡迎補充~

(文章來源于霍格沃茲測試學(xué)院)

更多技術(shù)文章可點擊

http://qrcode.testing-studio.com/f?from=jianshu&url=https://ceshiren.com/t/topic/3822

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市镇眷,隨后出現(xiàn)的幾起案子咬最,更是在濱河造成了極大的恐慌,老刑警劉巖欠动,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件永乌,死亡現(xiàn)場離奇詭異,居然都是意外死亡具伍,警方通過查閱死者的電腦和手機翅雏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來人芽,“玉大人望几,你說我怎么就攤上這事∮┨” “怎么了橄抹?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵靴迫,是天一觀的道長。 經(jīng)常有香客問我楼誓,道長玉锌,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任疟羹,我火速辦了婚禮主守,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘榄融。我一直安慰自己参淫,他們只是感情好,可當我...
    茶點故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布愧杯。 她就那樣靜靜地躺著涎才,像睡著了一般。 火紅的嫁衣襯著肌膚如雪民效。 梳的紋絲不亂的頭發(fā)上憔维,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天涛救,我揣著相機與錄音畏邢,去河邊找鬼。 笑死检吆,一個胖子當著我的面吹牛舒萎,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蹭沛,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼臂寝,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了摊灭?” 一聲冷哼從身側(cè)響起咆贬,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎帚呼,沒想到半個月后掏缎,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡煤杀,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年眷蜈,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片沈自。...
    茶點故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡酌儒,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出枯途,到底是詐尸還是另有隱情忌怎,我是刑警寧澤籍滴,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站呆躲,受9級特大地震影響异逐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜插掂,卻給世界環(huán)境...
    茶點故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一灰瞻、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧辅甥,春花似錦酝润、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至夏块,卻和暖如春疏咐,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背脐供。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工浑塞, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人政己。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓酌壕,卻偏偏與公主長得像,于是被迫代替她去往敵國和親歇由。 傳聞我的和親對象是個殘疾皇子卵牍,可洞房花燭夜當晚...
    茶點故事閱讀 44,914評論 2 355