Jenkins+RobotFramework 失敗用例重執(zhí)行方案

背景:接口測試用例運(yùn)行在Jenkins節(jié)點(diǎn)上蝴韭,在某些情況下够颠,比如網(wǎng)絡(luò)波動(dòng)等原因,會(huì)導(dǎo)致用例運(yùn)行失敗榄鉴,此時(shí)會(huì)觸發(fā)郵件和釘釘預(yù)警履磨,通知給到責(zé)任人,按照現(xiàn)有策略庆尘,當(dāng)本次構(gòu)建失敗時(shí)剃诅,會(huì)立馬觸發(fā)第二次構(gòu)建活動(dòng),若第二次構(gòu)建仍然失敗驶忌,則會(huì)再次觸發(fā)預(yù)警信息矛辕。在這種策略下,會(huì)導(dǎo)致相關(guān)責(zé)任人收到一些額外的無意義預(yù)警信息(如第一次構(gòu)建超時(shí)付魔,而第二次構(gòu)建成功)聊品,所以就多寫了一個(gè)腳本,在Jenkins中作為Robotframework用例的運(yùn)行入口几苍,當(dāng)有用例執(zhí)行失敗時(shí)翻屈,在所有cases執(zhí)行完成后,會(huì)選擇本次運(yùn)行失敗的cases再重試一次擦剑,然后合并兩次的測試報(bào)告文件妖胀。

腳本內(nèi)容很簡單芥颈,可拓展性很強(qiáng):

#!/usr/bin/env python
# -*- coding:utf8 -*-

import getopt
import os
import sys
from pathlib import Path
from robot.api import ExecutionResult

def parse_args() -> tuple:
    """解析命令行傳入的參數(shù)"""
    opts, args = getopt.getopt(sys.argv[1:], '-i:-e:-F:-E:', ["includeTag=", "excludeTag=", "format=", "env="])

    try:
        target = args[0]
    except IndexError:
        target = "./"

    def _parse(option, default_value=None):
        if isinstance(option, tuple):
            temp = [opt_value for (opt_name, opt_value) in opts if opt_name in option]
        else:
            temp = [opt_value for (opt_name, opt_value) in opts if opt_name == option]

        return temp[0] if len(temp) > 0 else default_value

    include_tag = _parse(("-i", "--includeTag"))  # 包含用例標(biāo)簽
    exclude_tag = _parse(("-e", "--excludeTag"))  # 排除用例標(biāo)簽
    env = _parse(("-E", "--env"), 'm')  # 用例運(yùn)行環(huán)境
    fm = _parse(("-F", "--format"), 'robot')  # 用例文件后綴名

    return include_tag, exclude_tag, env, fm, target


def first_run(target, env, include_tag, exclude_tag, fm):
    """首次運(yùn)行用例
    項(xiàng)目的基本目錄結(jié)構(gòu)是固定的, 在命令行中寫死了變量文件的相對路徑.
    """
    if include_tag:
        cmd = f"robot -F {fm} -i {include_tag} --output output_origin.xml --log NONE --report NONE -V variables.py:{env} -V ../Common/Variables/commonVars.py:{env} {target}"
    elif exclude_tag is not None:
        cmd = f"robot -F {fm} -e {exclude_tag} --output output_origin.xml --log NONE --report NONE -V variables.py:{env} -V ../Common/Variables/commonVars.py:{env} {target}"
    else:
        cmd = f"robot -F {fm} --output output_origin.xml --log NONE --report NONE -V variables.py:{env} -V ../Common/Variables/commonVars.py:{env} {target}"

    print(f'First run cmd >>>> {cmd}')
    os.system(cmd)


def parse_robot_result(xml_path) -> bool:
    """解析用例運(yùn)行結(jié)果"""
    suite = ExecutionResult(xml_path).suite

    fail = {}
    for test in suite.tests:
        if test.status == "FAIL":
            fail.update({test.name: test.status})

    all_tests = suite.statistics.critical
    print("*" * 50)
    print("當(dāng)前運(yùn)行目錄為: ", os.getcwd())
    print("總測試條數(shù):{0}, 初次運(yùn)行時(shí),通過的用例數(shù): {1}, 失敗的用例數(shù): {2}".format(all_tests.total, all_tests.passed, all_tests.failed))
    if all_tests.failed > 0:
        print("其中失敗的用例信息為: %s" % str(fail))
    print("*" * 50)

    return all_tests.failed > 0


def rerun_fail_case(target, env, include_tag, exclude_tag, fm):
    """ # TODO
    如果要重新運(yùn)行整個(gè)套件赚抡,需要使用`rerunfailedsuites`, 如果只想重新運(yùn)行失敗的測試用例而不是套件中已通過的測試爬坑,則使用`rerunfailed`(必須保證case是獨(dú)立的)
    -R, --rerunfailed <file>
        Selects failed tests from an earlier output file to be re-executed.
    -S, --rerunfailedsuites <file>
        Selects failed test suites from an earlier output file to be re-executed.
    """

    if include_tag:
        cmd = f"robot -F {fm} -i {include_tag} -V variables.py:{env} -V ../Common/Variables/commonVars.py:{env} --rerunfailed output_origin.xml --output output_rerun.xml {target}"
    elif exclude_tag is not None:
        cmd = f"robot -F {fm} -e {exclude_tag} -V variables.py:{env} -V ../Common/Variables/commonVars.py:{env} --rerunfailed output_origin.xml --output output_rerun.xml {target}"
    else:
        cmd = f"robot -F {fm} -V variables.py:{env} -V ../Common/Variables/commonVars.py:{env} --rerunfailed output_origin.xml --output output_rerun.xml {target}"

    print(f'重復(fù)運(yùn)行失敗的用例: {cmd}')
    os.system(cmd)
    """再次運(yùn)行失敗的用例"""


def merge_output():
    """合并xml文件,并生成測試報(bào)告
    注意集成到j(luò)enkins中時(shí)涂臣,需要指定 Output xml name為merge.xml
    """
    os.system("rebot --merge --output merge.xml *.xml")


def main():
    include_tag, exclude_tag, env, fm, target = parse_args()

    # 切換到output目錄
    if Path(target).is_dir():
        os.chdir(Path(target))
    else:
        os.chdir(Path(target).parent)

    for xml in Path.cwd().glob("*.xml"):
        os.remove(xml)

    first_run(target, env, include_tag, exclude_tag, fm)

    failed = parse_robot_result("output_origin.xml")
    if failed:
        rerun_fail_case(target, env, include_tag, exclude_tag, fm)

    # 不論是否存在失敗的用例, 都會(huì)合并測試報(bào)告
    merge_output()


if __name__ == '__main__':
    main()

-E參數(shù)外盾计,其他都是robot提供的的命令行參數(shù),在項(xiàng)目中使用了變量文件赁遗,來使得用例支持切換運(yùn)行環(huán)境署辉,-E參數(shù)需要傳入用例運(yùn)行的環(huán)境,-i-e參數(shù)用來傳入標(biāo)簽岩四,過濾本次要運(yùn)行的測試用例哭尝,可以傳入多個(gè)標(biāo)簽,如:H5ANDP1剖煌、H5ORMini材鹦、NotPaid等。

在Jenkins項(xiàng)目配置中耕姊,構(gòu)建操作配置的 Execute Windows Batch Cmd 如下:

cd %WORKSPACE%/ParkTest/interface
python runrobot.py --env=%Env% -F robot -i %Tag% ./
exit 0

EnvTag都是在參數(shù)化構(gòu)建時(shí)傳入的桶唐,并且設(shè)有默認(rèn)值。

在構(gòu)建后操作中茉兰,使用Robot Framework插件收集構(gòu)建結(jié)果尤泽,由于上面在腳本中修改了默認(rèn)的輸出文件名,這里要對應(yīng)進(jìn)行配置规脸,如下

當(dāng)項(xiàng)目第一次構(gòu)建失敗時(shí)坯约,第二次構(gòu)建只會(huì)運(yùn)行之前失敗的測試用例,并合并兩次生成的測試報(bào)告燃辖,在測試報(bào)告中展示如下:
log.html
我配置的郵件通知模板

這樣就可以減少一些無效的報(bào)錯(cuò)郵件了鬼店。

關(guān)于以上方案,有一點(diǎn)還要進(jìn)行特別說明黔龟,那就是項(xiàng)目中測試用例之前必須是相互獨(dú)立的妇智。保持Case獨(dú)立性我認(rèn)為是很有必要的,每一個(gè) Test Case 應(yīng)該只測試一種場景氏身,根據(jù)case復(fù)雜程度巍棱,不同場景同樣可大可小,但不能相互影響蛋欣。當(dāng)我們有隨機(jī)的跑其中某個(gè)Case或亂序的跑這些Cases時(shí)航徙,測試的結(jié)果都應(yīng)該是準(zhǔn)確的。Suite level和Directory level同樣要注意獨(dú)立性的問題陷虎。保持Case的獨(dú)立性到踏,這一點(diǎn)應(yīng)當(dāng)作為自動(dòng)化用例編寫規(guī)范杠袱,嚴(yán)格要求組內(nèi)其他成員。

【To be continud...】

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末窝稿,一起剝皮案震驚了整個(gè)濱河市楣富,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌伴榔,老刑警劉巖纹蝴,帶你破解...
    沈念sama閱讀 212,816評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異踪少,居然都是意外死亡塘安,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評論 3 385
  • 文/潘曉璐 我一進(jìn)店門援奢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來兼犯,“玉大人,你說我怎么就攤上這事萝究∶舛迹” “怎么了?”我有些...
    開封第一講書人閱讀 158,300評論 0 348
  • 文/不壞的土叔 我叫張陵帆竹,是天一觀的道長。 經(jīng)常有香客問我脓规,道長栽连,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,780評論 1 285
  • 正文 為了忘掉前任侨舆,我火速辦了婚禮秒紧,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘挨下。我一直安慰自己熔恢,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,890評論 6 385
  • 文/花漫 我一把揭開白布臭笆。 她就那樣靜靜地躺著叙淌,像睡著了一般。 火紅的嫁衣襯著肌膚如雪愁铺。 梳的紋絲不亂的頭發(fā)上鹰霍,一...
    開封第一講書人閱讀 50,084評論 1 291
  • 那天,我揣著相機(jī)與錄音茵乱,去河邊找鬼茂洒。 笑死,一個(gè)胖子當(dāng)著我的面吹牛瓶竭,可吹牛的內(nèi)容都是我干的督勺。 我是一名探鬼主播渠羞,決...
    沈念sama閱讀 39,151評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼智哀!你這毒婦竟也來了堵未?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,912評論 0 268
  • 序言:老撾萬榮一對情侶失蹤盏触,失蹤者是張志新(化名)和其女友劉穎渗蟹,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體赞辩,經(jīng)...
    沈念sama閱讀 44,355評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡雌芽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,666評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了辨嗽。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片世落。...
    茶點(diǎn)故事閱讀 38,809評論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖糟需,靈堂內(nèi)的尸體忽然破棺而出屉佳,到底是詐尸還是另有隱情,我是刑警寧澤洲押,帶...
    沈念sama閱讀 34,504評論 4 334
  • 正文 年R本政府宣布武花,位于F島的核電站,受9級(jí)特大地震影響杈帐,放射性物質(zhì)發(fā)生泄漏体箕。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,150評論 3 317
  • 文/蒙蒙 一挑童、第九天 我趴在偏房一處隱蔽的房頂上張望累铅。 院中可真熱鬧,春花似錦站叼、人聲如沸娃兽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽投储。三九已至,卻和暖如春翔试,著一層夾襖步出監(jiān)牢的瞬間轻要,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評論 1 267
  • 我被黑心中介騙來泰國打工垦缅, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留冲泥,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,628評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像凡恍,于是被迫代替她去往敵國和親志秃。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,724評論 2 351

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