淺談如何提高自動化測試的穩定性和可維護性 (pytest&allure)
裝飾器與出錯重試機制
談到穩定性,不得不說的就是“出錯重試”機制了,在自動化測試中,由於環境一般都是測試環境,經常會有各種各種的抽風情況影響測試結果,這樣就為測試的穩定性帶來了挑戰,畢竟誰也不想自己的指令碼一天到晚的出各種未知問題,而往往這種環境的抽風(通常是前端頁面的響應速度和後端介面的響應速度)帶來的影響是暫時的,可能上一秒失敗了,下一秒你再執行又好了,在這種情況下,如果你有一個出錯重試機制,起碼可以在這種暫時性的影響下讓你的指令碼安然無恙,下面我們具體的說一下做法。
什麼是裝飾器?
因為我們的做法依賴裝飾器,所以在去做之前,先簡單介紹一下裝飾器。
裝飾器,表現形式為,在方法(或者類)的上面加上
@retry
def test_login():
print("test")
error = 1/0
如果retry實現了出錯再次重試(稍後再說如何實現),那麼這麼使用的話,就會讓test_login這個case在執行出錯的時候再次執行。
很神奇,讓我們來看看實現retry的程式碼:
def retry(func): def warp(): for time in range(3): try: func() except: pass return warp
就結果而言,執行以下程式碼:
@retry
def test_login():
print("test")
error = 1/0
test_login()
和執行:
retry(test_login)()
是等價的,由此我們可以看出,裝飾器其實本質上就是一個函式,這個函式接收其他函式(或者類)作為引數,通過對這個函式(或者類)的呼叫或者修改,完成不更改原始函式而修改該函式的功能。
在這裡還有一個知識點,你有沒有想過,在retry內部的函式warp(),是怎麼拿到func這個引數來執行的?執行retry函式return的是warp這個函式,而warp並沒有接受func這個傳參啊。
這就是python裡的閉包的概念,閉包就是指執行時自帶上下文的函式,比如這裡的warp這個函式,他執行的時候自帶了上層函式retry傳給他的func這個函式,所以才可以在執行時對func進行處理和輸出。
瞭解了裝飾器和閉包,那麼下面就很容易做到對測試用例的出錯重試機制了。
如果對軟體測試、介面測試、自動化測試、效能測試、LR指令碼開發、面試經驗交流。感興趣可以來加群:747981058,群內會有不定期的發放免費的資料連結,這些資料都是從各個技術網站蒐集、整理出來的,如果你有好的學習資料可以私聊發我,我會註明出處之後分享給大家。
編寫一個出錯重試裝飾器
現在,我們來嘗試自己編寫一個用於測試用例的出錯重試裝飾器,程式碼如下:
def retry(times=3,wait_time=10):
def warp_func(func):
def fild_retry(*args,**kwargs):
for time in range(times):
try:
func(*args,**kwargs)
return
except:
time.sleep(wait_time)
return fild_retry
return warp_func
這個裝飾器可以通過傳入重試次數(times)和重試等待時間(wait_time),對待測用例實行重試機制。
pytest裡的出錯重試機制實現
在測試框架pytest裡,已經實現了有關出錯重試的策略,我們首先需要安裝一個此類的外掛,在cmd內執行以下命令安裝:
pip install pytest-rerunfailures
如果你需要將此機制應用到所有的用例上,那麼請在執行的時候使用如下命令(reruns是重試次數):
pytest --reruns 5
來執行你的用例;
如果你期望加上出錯重試的等待時間,請使用如下命令(reruns-delay是等待時間):
pytest --reruns 5 --reruns-delay 1
來執行你的用例;
如果你只想對某幾個測試用例應用重試策略,你可以使用裝飾器:
@pytest.mark.flaky(reruns=5, reruns_delay=2)
例如:
@pytest.mark.flaky(reruns=5, reruns_delay=2)
def test_example():
import random
assert random.choice([True, False])
更詳細的介紹請參閱官方文件 。
Allure裡的測試用例分層
剛剛我們實現了用例的出錯重試機制,但是這僅僅解決了指令碼在不穩定環境下的穩定性;如果還想要指令碼變得更加容易維護,除了傳統的po模式使用例和元素分離之外,我們還可以引入測試用例分層機制。
為什麼要採用分層機制?
傳統的po模式,僅僅實現了用例和元素分離,這一定層面上保障了用例的可維護性,起碼不必頭疼於元素的變更會讓用例到處失效;但是這還不夠,例如,現在有三個case,他們都包含了以下步驟:登入、開啟工作臺、進入個人中心;那麼如果不做分層,這三個用例會把這三個步驟都寫一遍,如果某天頁面的變動導致其中一個步驟需要更改,那麼你不得不去每個用例裡去更新那個步驟。
而如果,我們把用例當做是堆積木,登入、開啟工作臺、進入個人中心這三個步驟都只是個積木,那麼我們寫用例的時候,只需要在用到這個步驟時,把積木搭上去;如果某一天,其中一個積木的步驟有變動,那麼只需要去更改這個積木的內容,而無需在每個使用了這個積木的用例裡去改動。
這大大增強了用例的複用性和可維護性,這就是採用分層機制的原因,下面,我會就allure裡的分層機制做介紹來討論具體如何實現。
allure的裝飾器@step
在allure裡,我們可以通過裝飾器@step完成分層機制,具體的,當你用@step裝飾一個方法時,當你在用例裡執行這個方法,會在報告裡,表現出這個被裝飾方法;而@step支援巢狀結構,這就意味著,你可以像搭積木一樣去搭你的步驟,而他們都會一一在報告裡被展示。
下面直接用allure的官方示例作做舉例:
import allure
import pytest
from .steps import imported_step
@allure.step
def passing_step():
pass
@allure.step
def step_with_nested_steps():
nested_step()
@allure.step
def nested_step():
nested_step_with_arguments(1, 'abc')
@allure.step
def nested_step_with_arguments(arg1, arg2):
pass
def test_with_imported_step():
passing_step()
imported_step()
def test_with_nested_steps():
passing_step()
step_with_nested_steps()
執行這個case後,報告是這樣的:
可以看到,
test_with_nested_steps由passing_step()和step_with_nested_steps()這兩個方法組成;
而step_with_nested_steps()又由nested_step()組成;
nested_step()又由nested_step_with_arguments(1, 'abc')組成;
這樣就像搭積木一樣,組成了測試用例;而在報告裡,也層級分明的標識了步驟的巢狀結構。
這樣,我們就可以通過一個又一個@step裝飾的方法,組成測試用例;同時報告裡也會支援層級顯示;從而完成我們的分層機制。