淺談如何提高自動(dòng)化測(cè)試的穩(wěn)定性和可維護(hù)性(pytest&allure)

在之前,我寫(xiě)過(guò)一個(gè)系列“從零開(kāi)始搭建一個(gè)簡(jiǎn)單的ui自動(dòng)化測(cè)試框架(pytest+selenium+allure)”,在這個(gè)系列里,主要介紹了如何從零開(kāi)始去搭建一個(gè)可用的自動(dòng)化工程框架,但是還缺乏了一些細(xì)節(jié)的補(bǔ)充,例如對(duì)于自動(dòng)化測(cè)試而言,如何提高其測(cè)試的穩(wěn)定性?

本篇文章,將會(huì)和讀者一起探討這個(gè)問(wèn)題。

裝飾器與出錯(cuò)重試機(jī)制

談到穩(wěn)定性,不得不說(shuō)的就是“出錯(cuò)重試”機(jī)制了,在自動(dòng)化測(cè)試中,由于環(huán)境一般都是測(cè)試環(huán)境,經(jīng)常會(huì)有各種各種的抽風(fēng)情況影響測(cè)試結(jié)果,這樣就為測(cè)試的穩(wěn)定性帶來(lái)了挑戰(zhàn),畢竟誰(shuí)也不想自己的腳本一天到晚的出各種未知問(wèn)題,而往往這種環(huán)境的抽風(fēng)(通常是前端頁(yè)面的響應(yīng)速度和后端接口的響應(yīng)速度)帶來(lái)的影響是暫時(shí)的,可能上一秒失敗了,下一秒你再執(zhí)行又好了,在這種情況下,如果你有一個(gè)出錯(cuò)重試機(jī)制,起碼可以在這種暫時(shí)性的影響下讓你的腳本安然無(wú)恙,下面我們具體的說(shuō)一下做法。

什么是裝飾器?

因?yàn)槲覀兊淖龇ㄒ蕾囇b飾器,所以在去做之前,先簡(jiǎn)單介紹一下裝飾器。

裝飾器,表現(xiàn)形式為,在方法(或者類)的上面加上@xxx這樣的語(yǔ)句,假如我們已經(jīng)實(shí)現(xiàn)了一個(gè)裝飾器名叫retry,那么我們想用它就這么用:

@retry
def test_login():
    print("test")
    error = 1/0

如果retry實(shí)現(xiàn)了出錯(cuò)再次重試(稍后再說(shuō)如何實(shí)現(xiàn)),那么這么使用的話,就會(huì)讓test_login這個(gè)case在執(zhí)行出錯(cuò)的時(shí)候再次執(zhí)行。

很神奇,讓我們來(lái)看看實(shí)現(xiàn)retry的代碼:

def retry(func):
    def warp():
        for time in range(3):
            try:
                func()
            except:
                pass
    return warp
    

就結(jié)果而言,執(zhí)行以下代碼:

@retry
def test_login():
    print("test")
    error = 1/0

test_login()

和執(zhí)行:

retry(test_login)()

是等價(jià)的,由此我們可以看出,裝飾器其實(shí)本質(zhì)上就是一個(gè)函數(shù),這個(gè)函數(shù)接收其他函數(shù)(或者類)作為參數(shù),通過(guò)對(duì)這個(gè)函數(shù)(或者類)的調(diào)用或者修改,完成不更改原始函數(shù)而修改該函數(shù)的功能。

在這里還有一個(gè)知識(shí)點(diǎn),你有沒(méi)有想過(guò),在retry內(nèi)部的函數(shù)warp(),是怎么拿到func這個(gè)參數(shù)來(lái)執(zhí)行的?執(zhí)行retry函數(shù)return的是warp這個(gè)函數(shù),而warp并沒(méi)有接受func這個(gè)傳參啊。

這就是python里的閉包的概念,閉包就是指運(yùn)行時(shí)自帶上下文的函數(shù),比如這里的warp這個(gè)函數(shù),他運(yùn)行的時(shí)候自帶了上層函數(shù)retry傳給他的func這個(gè)函數(shù),所以才可以在運(yùn)行時(shí)對(duì)func進(jìn)行處理和輸出。

了解了裝飾器和閉包,那么下面就很容易做到對(duì)測(cè)試用例的出錯(cuò)重試機(jī)制了。

編寫(xiě)一個(gè)出錯(cuò)重試裝飾器

現(xiàn)在,我們來(lái)嘗試自己編寫(xiě)一個(gè)用于測(cè)試用例的出錯(cuò)重試裝飾器,代碼如下:

def retry(times=3,wait_time=10):
    def warp_func(func):
        def fild_retry(*args,**kwargs):
            for t in range(times):
                try:
                    func(*args,**kwargs)
                    return 
                except:
                    time.sleep(wait_time)
        return fild_retry
    return warp_func

這個(gè)裝飾器可以通過(guò)傳入重試次數(shù)(times)和重試等待時(shí)間(wait_time),對(duì)待測(cè)用例實(shí)行重試機(jī)制。

pytest里的出錯(cuò)重試機(jī)制實(shí)現(xiàn)

在測(cè)試框架pytest里,已經(jīng)實(shí)現(xiàn)了有關(guān)出錯(cuò)重試的策略,我們首先需要安裝一個(gè)此類的插件,在cmd內(nèi)執(zhí)行以下命令安裝:

pip install pytest-rerunfailures

如果你需要將此機(jī)制應(yīng)用到所有的用例上,那么請(qǐng)?jiān)趫?zhí)行的時(shí)候使用如下命令(reruns是重試次數(shù)):

pytest --reruns 5

來(lái)執(zhí)行你的用例;

如果你期望加上出錯(cuò)重試的等待時(shí)間,請(qǐng)使用如下命令(reruns-delay是等待時(shí)間):

pytest --reruns 5 --reruns-delay 1

來(lái)執(zhí)行你的用例;

如果你只想對(duì)某幾個(gè)測(cè)試用例應(yīng)用重試策略,你可以使用裝飾器:

@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])

更詳細(xì)的介紹請(qǐng)看官方文檔 。

Allure里的測(cè)試用例分層

剛剛我們實(shí)現(xiàn)了用例的出錯(cuò)重試機(jī)制,但是這僅僅解決了腳本在不穩(wěn)定環(huán)境下的穩(wěn)定性;如果還想要腳本變得更加容易維護(hù),除了傳統(tǒng)的po模式使用例和元素分離之外,我們還可以引入測(cè)試用例分層機(jī)制。

為什么要采用分層機(jī)制?

傳統(tǒng)的po模式,僅僅實(shí)現(xiàn)了用例和元素分離,這一定層面上保障了用例的可維護(hù)性,起碼不必頭疼于元素的變更會(huì)讓用例到處失效;但是這還不夠,例如,現(xiàn)在有三個(gè)case,他們都包含了以下步驟:登錄、打開(kāi)工作臺(tái)、進(jìn)入個(gè)人中心;那么如果不做分層,這三個(gè)用例會(huì)把這三個(gè)步驟都寫(xiě)一遍,如果某天頁(yè)面的變動(dòng)導(dǎo)致其中一個(gè)步驟需要更改,那么你不得不去每個(gè)用例里去更新那個(gè)步驟。

而如果,我們把用例當(dāng)做是堆積木,登錄、打開(kāi)工作臺(tái)、進(jìn)入個(gè)人中心這三個(gè)步驟都只是個(gè)積木,那么我們寫(xiě)用例的時(shí)候,只需要在用到這個(gè)步驟時(shí),把積木搭上去;如果某一天,其中一個(gè)積木的步驟有變動(dòng),那么只需要去更改這個(gè)積木的內(nèi)容,而無(wú)需在每個(gè)使用了這個(gè)積木的用例里去改動(dòng)。

這大大增強(qiáng)了用例的復(fù)用性和可維護(hù)性,這就是采用分層機(jī)制的原因,下面,我會(huì)就allure里的分層機(jī)制做介紹來(lái)討論具體如何實(shí)現(xiàn)。

allure的裝飾器@step

在allure里,我們可以通過(guò)裝飾器@step完成分層機(jī)制,具體的,當(dāng)你用@step裝飾一個(gè)方法時(shí),當(dāng)你在用例里執(zhí)行這個(gè)方法,會(huì)在報(bào)告里,表現(xiàn)出這個(gè)被裝飾方法;而@step支持嵌套結(jié)構(gòu),這就意味著,你可以像搭積木一樣去搭你的步驟,而他們都會(huì)一一在報(bào)告里被展示。

下面直接用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()

運(yùn)行這個(gè)case后,報(bào)告是這樣的:


image

可以看到,
test_with_nested_steps由passing_step()和step_with_nested_steps()這兩個(gè)方法組成;

而step_with_nested_steps()又由nested_step()組成;

nested_step()又由nested_step_with_arguments(1, 'abc')組成;

這樣就像搭積木一樣,組成了測(cè)試用例;而在報(bào)告里,也層級(jí)分明的標(biāo)識(shí)了步驟的嵌套結(jié)構(gòu)。

這樣,我們就可以通過(guò)一個(gè)又一個(gè)@step裝飾的方法,組成測(cè)試用例;同時(shí)報(bào)告里也會(huì)支持層級(jí)顯示;從而完成我們的分層機(jī)制。

有關(guān)@step的更多詳細(xì)介紹請(qǐng)參閱官方文檔

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,695評(píng)論 19 139
  • 文章來(lái)自:http://blog.csdn.net/mj813/article/details/52451355 ...
    好大一只鵬閱讀 9,375評(píng)論 2 126
  • 1****、問(wèn):你在測(cè)試中發(fā)現(xiàn)了一個(gè)bug****,但是開(kāi)發(fā)經(jīng)理認(rèn)為這不是一個(gè)bug****,你應(yīng)該怎樣解決? 首...
    蛋炒飯_By閱讀 5,402評(píng)論 1 94
  • 張艷 焦點(diǎn)網(wǎng)絡(luò)中級(jí)7期 堅(jiān)持分享第137天 深刻的感受到學(xué)習(xí)可以控制情緒,哪一天沒(méi)有看書(shū),學(xué)習(xí),碰到孩子不太乖...
    柚橙媽咪閱讀 208評(píng)論 0 2
  • 親愛(ài)的小胖,今天媽媽的死黨少燕阿姨把媽媽饞得不行的肉丸和肉餅寄來(lái)了,可是,因?yàn)榘⒁虥](méi)有經(jīng)驗(yàn),加上天氣熱,結(jié)果...
    zhuo舍舍閱讀 219評(píng)論 0 0

友情鏈接更多精彩內(nèi)容