日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > linux >内容正文

linux

使用 Python 在 Linux 上实现一键回归测试

發(fā)布時間:2025/3/20 linux 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 使用 Python 在 Linux 上实现一键回归测试 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

從代碼庫遷出代碼 —- pexpect 的使用
測試人員從代碼庫(例如 CVS )遷出代碼的過程中,需要手動輸入訪問密碼,而 Python 提供了 Pexpect 模塊則能夠?qū)⑹謩虞斎朊艽a這一過程自動化。當(dāng)然 Pexpect 也可以用來和 ssh、ftp、passwd、telnet 等命令行進(jìn)行自動化交互。這里我們以 CVS 為例展示如何利用 Pexpect 從代碼庫遷出代碼。

清單 1. 用 pexpect 遷出代碼庫代碼

try: chkout_cmd = 'cvs co project_code' #從代碼庫遷出 project_code 的內(nèi)容 child = pexpect.spawn(chkout_cmd) child.expect('password:') child.sendline('your-password') #請?zhí)鎿Q"your-password"為真實(shí)密碼 child.interact() except:pass #忽略遷出代碼中的錯誤

在清單 1 中,我們用命令”cvs co project_code”從代碼庫中遷出了 project_code 的內(nèi)容,我們也可以用該命令來更新已經(jīng)遷出的代碼。只需要將命令”cvs update” 傳給類 pexpect.spawn()即可,詳細(xì)的實(shí)現(xiàn)請參考代碼文件。這里 interact()函數(shù)是必須的,用來在交互的方式下控制該子進(jìn)程。有時代碼庫中會存在目錄不一致行情況,遷出代碼會因報錯終止,所以需要異常處理(try … execpt)來忽略該錯誤。

編譯代碼和運(yùn)行測試腳本 —- subprocess 的使用
測試人員獲取最新的代碼之后,就要對源碼進(jìn)行編譯,并且運(yùn)行測試用例。Python 語言提供了多種方法如 os.system()/os.popen()來執(zhí)行一條命令,這里我們推薦用 subprocess 模塊來創(chuàng)建子進(jìn)程,完成代碼編譯和運(yùn)行測試用例。因?yàn)?subprocess 支持主進(jìn)程和子進(jìn)程的交互,同時也支持主進(jìn)程和子進(jìn)程是同步執(zhí)行還是異步執(zhí)行。由于本文中的各個功能模塊有都先后依賴關(guān)系,所以全部采用的是主進(jìn)程和子進(jìn)程同步模式執(zhí)行。

編譯代碼

清單 2. 用 subprocess 編譯代碼

''' 遇到問題沒人解答?小編創(chuàng)建了一個Python學(xué)習(xí)交流QQ群:579817333 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學(xué)習(xí)教程和PDF電子書! ''' build_cmd = 'build_command_for_your_code' #請?jiān)谶@里配置編譯命令 build_proc = subprocess.Popen(build_cmd, stdin=None, stdout=None, stderr=None, shell=True) build_proc.wait() #等待子進(jìn)程結(jié)束assert (0 == build_proc.returncode)

在一些系統(tǒng)中我們編譯代碼采用的是腳本文件(如 shell 腳本),那么我們?nèi)匀豢梢匀缦旅顏硗瓿纱a編譯工作。

清單 3. 用 subprocess 的 call 函數(shù)執(zhí)行腳本文件

subprocess.call(["code_compile.sh"])

運(yùn)行測試腳本

在編譯完成代碼之后,我們同樣可以調(diào)用 subprocess.Popen 來創(chuàng)建子進(jìn)程運(yùn)行測試用例。如果測試人員的測試用例已經(jīng)寫成了測試?yán)_本,我們則可以用 subprocess.call()來執(zhí)行測試?yán)_本文件,代碼實(shí)現(xiàn)就不再贅述。有些系統(tǒng)會直接把詳細(xì)日志輸出到屏幕上,那么我們可以用重定向命令”2>&1″把屏幕輸出寫文件。

清單 4. 用重定向命令把輸出寫文件

ut_cmd = 'Your_unit_test_command 2>&1 > %s' %self.debug_log #debug_log 定義在__init__函數(shù)中,用來存儲詳細(xì)日志

測試結(jié)果存儲和發(fā)布 —- XML 解析
我們的項(xiàng)目采用敏捷開發(fā),為了更好的反應(yīng)敏捷開發(fā)周期,我們希望存儲日志的目錄名不但能夠指明的具體日期,同時也能反映敏捷(迭代)開發(fā)階段,這樣相關(guān)人員在查看相應(yīng)目錄中的日志時,能夠清楚的明白日志實(shí)在在哪個迭代周期的哪一天產(chǎn)生的。本文使用文件 summary 作為運(yùn)行測試用例后生成的匯總?cè)罩?#xff0c;用文件 log.txt 用來存儲詳細(xì)日志。如下圖所示,在共享目錄 SharedFiles 中存儲了一些列迭代周期中的日志。

清單 5. 共享目錄結(jié)構(gòu)

SharedFiles ├── Sprint10-20130823121500 │ ├── log.txt │ └── summary ├── Sprint10-20130826152715 │ ├── log.txt │ └── summary ├── Sprint10-20130828165235

為了能夠讓目錄名反映敏捷開發(fā)周期,我們需要自己定義一個配置文件(txt 或 xml 均可)。由于 Python 已經(jīng)很好的支持了 XML 解析,并且 XML 文件作為配置也是當(dāng)前的流行趨勢。本文就以 XML 解析為例進(jìn)行說明。本文使用的 XML 文件名是 Sprint.xml,清單 6 是該 xml 的概要內(nèi)容

清單 6. Sprint.xml 文件結(jié)構(gòu)

<sprint-schedule> <min-sprint>10</min-sprint> <max-sprint>20</max-sprint> <sprint10>20130814</sprint10><sprint11>20130828</sprint11> … … <sprint19>20131218</sprint19> <sprint20>20140101</sprint20></sprint-schedule>

關(guān)于 xml 解析 Python 提供了多種方法。本文采用 minidom 對 xml 文件進(jìn)行解析,清單 7 是相關(guān)處理代碼。

清單 7. xml 解析代碼

''' 遇到問題沒人解答?小編創(chuàng)建了一個Python學(xué)習(xí)交流QQ群:579817333 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學(xué)習(xí)教程和PDF電子書! ''' cur_date = time.strftime('%Y%m%d%H%M%S', time.localtime(time.time())) # 首先獲取當(dāng)前系統(tǒng)日期xmldoc = minidom.parse(xml_file) min_num_node = xmldoc.getElementsByTagName('min-sprint')[0] min_num = int(min_num_node.firstChild.data) #解析出迭代開發(fā)周期的起始周期max_num_node = xmldoc.getElementsByTagName('max-sprint')[0] max_num = int(max_num_node.firstChild.data) #解析出迭代開發(fā)周期的終止周期cur_num = min_num #遍歷所有迭代周期,取出當(dāng)前迭代周期的開始時間和當(dāng)前的系統(tǒng)時間對比,從而確定當(dāng)前位于哪一個迭代周期。 while cur_num <= max_num : node_name = 'sprint' + str(cur_num) cur_node = xmldoc.getElementsByTagName(node_name)[0] sprint_date = cur_node.firstChild.data if sprint_date < cur_date[0:7]: cur_num = cur_num + 1 else:break

這樣 cur_num 就指向了當(dāng)前的迭代開發(fā)周期。然后,我們就可以根據(jù)當(dāng)前日期和開發(fā)階段創(chuàng)建對應(yīng)的日志目錄名了,最后把運(yùn)行結(jié)果存儲到該目錄下,參見清單 8 實(shí)現(xiàn)。

清單 8. 日志存儲代碼

log_dir = self.share_dir + '/Sprint' + str(cur_num) + '-' + cur_date #share_dir 為共享目錄,定義在初始化函數(shù)中 os.mkdir(log_dir) os.system('mv %s %s' %(self.debug_fullname, log_dir)) #debug_fullname,詳細(xì)日志文件名(含目錄),定義在初始化函數(shù)中os.system('mv %s %s' %(self.sum_fullname, log_dir)) #sum_fullname,匯總?cè)罩镜娜窂轿募?#xff0c;定義在初始化函數(shù)中

關(guān)于測試結(jié)果的發(fā)布,本文并沒有把測試結(jié)果以自動化的形式發(fā)送郵件,而是手動在每個開發(fā)周期結(jié)束時,群發(fā)郵件給相關(guān)人員。或者在驗(yàn)證失敗后,通知相關(guān)的開發(fā)人員,這是由于作者所在團(tuán)隊(duì)項(xiàng)目代碼提交頻率不是很高。在更大型的項(xiàng)目中,往往需要增加自動發(fā)送郵件的功能,相關(guān)實(shí)現(xiàn)本文不再贅述。

也談界面設(shè)計 —- getopt 的使用
在日常的測試過程中,我們并不是每次都要遷出代碼,編譯代碼,運(yùn)行測試用例和收集測試結(jié)果。這樣就需要我們能夠有選擇的運(yùn)行部分程序功能,例如只運(yùn)行測試用例和收集結(jié)果。這里我們提供了 4 個運(yùn)行選澤:

選項(xiàng) 1:遷出代碼–>編譯版本–>運(yùn)行測試用例–>收集測試結(jié)果

選項(xiàng) 2:更新代碼–>編譯版本–>運(yùn)行測試用例–>收集測試結(jié)果

選項(xiàng) 3:編譯版本–>運(yùn)行測試用例–>收集測試結(jié)果

選項(xiàng) 4:運(yùn)行測試用例–>收集測試結(jié)果

當(dāng)然我們還需要提供幫助信息,以方便不熟悉該腳本實(shí)現(xiàn)的人員使用。python 也提供了 getopt 模塊讓我們輕松實(shí)現(xiàn)上述功能。實(shí)現(xiàn)代碼參見清單 9

清單 9. 命令行寫解析代碼

''' 遇到問題沒人解答?小編創(chuàng)建了一個Python學(xué)習(xí)交流QQ群:579817333 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學(xué)習(xí)教程和PDF電子書! ''' try:opts, args = getopt.getopt(sys.argv[1:], 'bchu', ['build', 'checkout', 'help', 'update'])except getopt.error, msg:self.usage()sys.exit(2)build_flag = 0 #構(gòu)建選項(xiàng)for o, a in opts:if o in ('-h', '--help'):self.usage()sys.exit()elif o in ('-c', '--checkout'):print "執(zhí)行操作:遷出代碼-->編譯版本-->運(yùn)行測試用例-->收集測試結(jié)果"build_flag = 1breakelif o in ('-u', '--update'):print "執(zhí)行操作:更新代碼-->編譯版本-->運(yùn)行測試用例-->收集測試結(jié)果"build_flag = 2breakelif o in ('-b', '--build'):print "執(zhí)行操作:編譯版本-->運(yùn)行測試用例-->收集測試結(jié)果"build_flag = 3breakelse:self.usage()sys.exit()if (0 == build_flag) :if 2 <= len(sys.argv):self.usage()sys.exit()raw_input('\n 按 Enter 鍵繼續(xù)。。。(Ctrl+C 退出)\t')if (1 == build_flag) : #遷出代碼,并編譯代碼self.checkout_code()self.build_code()elif (2 == build_flag) : #更新代碼,并編譯代碼self.update_code()self.build_code()elif (3 == build_flag) : #編譯代碼self.build_code()#運(yùn)行測試用例并收集運(yùn)行結(jié)果self.set_python()self.run_testsuite()self.store_logs()

如果我們在運(yùn)行的過程中想中斷(如利用 Ctrl+C)一鍵回歸測試進(jìn)程的執(zhí)行時,有時我們會發(fā)現(xiàn)雖然主進(jìn)程已經(jīng)被終止,但子進(jìn)程仍在運(yùn)行。我們能否在中斷主進(jìn)程的同時也中斷子進(jìn)程呢?答案當(dāng)然是肯定的,我們可以用信號處理函數(shù)捕獲信號(如捕獲 Ctrl+C 產(chǎn)生的中斷信號),然后在顯式終止對應(yīng)的子進(jìn)程。這里就需要我們在創(chuàng)建子進(jìn)程的時候,先保存子進(jìn)程 ID,當(dāng)然把子進(jìn)程 ID 保存到初始化函數(shù)中,是個不錯的選擇,清單 10 是相關(guān)實(shí)現(xiàn)。

清單 10. 信號處理代碼

''' 遇到問題沒人解答?小編創(chuàng)建了一個Python學(xué)習(xí)交流QQ群:579817333 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學(xué)習(xí)教程和PDF電子書! ''' # 終止子進(jìn)程的運(yùn)行 def handler(self, signum, frame):if (-1 != self.subproc_id) : #subproc_id 定義在初始化函數(shù)中,用來存儲當(dāng)前子進(jìn)程的 IDos.killpg(self.subproc_id, signal.SIGINT)sys.exit(-1)

這里我們需要在初始化函數(shù)中注冊要捕獲的信號,并且創(chuàng)建成員變量用來保存子進(jìn)程的 ID,詳細(xì)實(shí)現(xiàn)請參見清單 11。

基于對象的設(shè)計 —- class 的使用

最后終于輪到 class 登場了,提到 class 我們就不能不談構(gòu)造函數(shù)(初始化函數(shù))和析構(gòu)函數(shù)。之前我們多次提到初始化函數(shù),初始化函數(shù)允許我們定義一些變量,這些變量在整個類對象的生存周期內(nèi)均有效。由于本文沒有向系統(tǒng)申請資源,就再不定義析構(gòu)函數(shù)了。

清單 11. 初始化處理代碼

def __init__(self):signal.signal(signal.SIGINT, self.handler) #注冊需要捕獲的信號量self.myafs_dir = os.getenv('myafs')self.subproc_id = -1 #子進(jìn)程 ID,用來在終止主進(jìn)程時也同時終止子進(jìn)程self.debug_log = 'log.txt' #存儲詳細(xì)運(yùn)行日志的文件名self.debug_fullname = os.getcwd() + os.sep + self.debug_log #全路徑文件名(假設(shè)產(chǎn)生在該目錄下)self.sum_log = 'summary' #存儲匯總?cè)罩镜奈募?/span>self.sum_fullname = os.getcwd() + os.sep + self.sum_log #全路徑文件名(假設(shè)產(chǎn)生在當(dāng)前目錄下)self.share_dir = self.utafs_dir + '/SharedFiles' #共享目錄文件名

通常我們不需要太關(guān)注設(shè)計風(fēng)格,只要 Python 腳本能完成我們的測試要求即可。對于較小的腳本,幾條 Python 指令順序執(zhí)行即可。為了模塊功能復(fù)用和可讀性,我們通常會把功能模塊封裝成函數(shù)。本文將實(shí)現(xiàn)的所有函數(shù)都封裝到一個類中,使得該腳本更加一體化。

清單 12. class 框架結(jié)構(gòu)代碼

''' 遇到問題沒人解答?小編創(chuàng)建了一個Python學(xué)習(xí)交流QQ群:579817333 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學(xué)習(xí)教程和PDF電子書! ''' class COneClickRegTest:#設(shè)定一些經(jīng)常使用的變量,如當(dāng)前工作目錄,日志名稱、存儲路徑等def __init__(self):#設(shè)定 python 環(huán)境變量,實(shí)現(xiàn)參見代碼文件def set_python(self):#更新代碼,實(shí)現(xiàn)參見代碼文件def update_code(self):#遷出代碼,實(shí)現(xiàn)參見第 2 章代碼def checkout_code(self):#編譯版本,實(shí)現(xiàn)參見清單 1 代碼def build_code(self):#運(yùn)行測試集,實(shí)現(xiàn)參見代碼文件def run_testsuite(self):#存儲運(yùn)行結(jié)果,實(shí)現(xiàn)參見清單 7 和清單 8 代碼def store_logs(self):#信號處理,實(shí)現(xiàn)參見清單 10 代碼def handler(self, signum, frame):#腳本使用說明,實(shí)現(xiàn)參見代碼文件def usage(self):#命令行解析以及執(zhí)行對應(yīng)的功能,實(shí)現(xiàn)參見清單 9 代碼def main(self):

總結(jié)

以上是生活随笔為你收集整理的使用 Python 在 Linux 上实现一键回归测试的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。