【ZStack】16.自動化測試系統(tǒng)3——基于模型的測試

模型測試系統(tǒng)是zstack-woodpecker中的一個子項目。通過有限狀態(tài)機和行為選擇策略,它可以產(chǎn)生隨機的API操作,一直運行下去,直到遇到一個缺陷或者預(yù)定義的退出條件。ZStack依賴模型測試去測試真實世界中難以遇到的邊界用例,在測試覆蓋度方面補充集成測試和系統(tǒng)測試。

概述

測試覆蓋率是一個判斷一個測試系統(tǒng)品質(zhì)的重要指示器。常規(guī)測試方法論,例如單元測試,集成測試,系統(tǒng)測試,都是由人類邏輯思考構(gòu)建的,難以覆蓋軟件中的邊界場景。這個問題在IaaS軟件中變得更加明顯,因為管理不同的子系統(tǒng)會導(dǎo)致極為復(fù)雜的場景。
ZStack通過引入基于模型的測試來解決這個問題。它可以產(chǎn)生由隨機API組合構(gòu)成的場景,會持續(xù)運行知道遇到預(yù)定義的退出條件或者找到一個缺陷。作為機器驅(qū)動的測試,基于模型的測試可以克服人類邏輯思考的缺陷來執(zhí)行一些,看起來反人類邏輯,但是API完全正確的測試,幫助發(fā)現(xiàn)難以被人類主導(dǎo)的測試發(fā)現(xiàn)的邊界問題。

一個例子可以幫助理解這個思想。基于模型的測試系統(tǒng)通常在執(zhí)行大約200個API后暴露一個bug,在調(diào)試后,我們找到最小重現(xiàn)這個問題的序列是:

  1. 創(chuàng)建一個VM
  2. 關(guān)閉這個VM
  3. 為這個VM的根云盤創(chuàng)建一個云盤快照
  4. 從這個VM的根云盤創(chuàng)建一個新的數(shù)據(jù)云盤快照
  5. 銷毀這個VM
  6. 創(chuàng)建一個新的數(shù)據(jù)云盤,使用4中的模板
  7. 從6中的數(shù)據(jù)云盤創(chuàng)建一個新的云盤快照

這個操作序列顯然是反邏輯的,我們相信沒有測試者會寫一個集成測試用例或者系統(tǒng)測試用例這么做。這就是機器思考閃光的地方,因為它沒有人類的感情,會做人類感覺不合理的事情。在找到這個bug之后,我們生成了一個回歸測試為以后保障這個問題。

基于模型的測試系統(tǒng)

基于模型的測試系統(tǒng),因為由機器驅(qū)動,也被稱為機器人測試。當(dāng)這個系統(tǒng)運行時,它從一個模型(在下面幾節(jié)中也被稱為階段)移動到另一個模型,通過執(zhí)行被動作選擇策略選出的動作(也被稱為操作)。在每一個模型完成之后,檢查器將會驗證測試結(jié)果,測試退出條件。如果任何失敗被發(fā)現(xiàn),或者退出條件被滿足,系統(tǒng)將會退出。否則,它將會移動到下一個模型,然后重復(fù)。

有限狀態(tài)機

在基于模型的測試的理論中,有許許多多生成測試操作的方式。例如:有限狀態(tài)機,自動推導(dǎo),模型檢驗。我們選擇使用有限狀態(tài)機,因為它自然地適合IaaS軟件,其中每一個資源都由狀態(tài)驅(qū)動。例如,從用戶角度看,VM的狀態(tài)像這樣:

在基于模型的測試系統(tǒng)中,每一個資源的每一個狀態(tài)都預(yù)先定義在test_state.py中,看起來像:

vm_state_dict = {
            Any: 1 ,
            vm_header.RUNNING: 2,
            vm_header.STOPPED: 3,
            vm_header.DESTROYED: 4
            }

    vm_volume_state_dict = {
            Any: 10,
            vm_no_volume_att: 20,
            vm_volume_att_not_full: 30,
            vm_volume_att_full: 40
            }

    volume_state_dict = {
            Any: 100,
            free_volume: 200,
            no_free_volume:300
            }

    image_state_dict = {
            Any: 1000,
            no_new_template_image: 2000,
            new_template_image: 3000
            }

系統(tǒng)中所有資源的所有狀態(tài)構(gòu)成一個階段(模型),系統(tǒng)可以從一個階段轉(zhuǎn)移到下一個階段,通過執(zhí)行維護在轉(zhuǎn)換表中操作。一個階段被定義成類似這樣:

class TestStage(object):
    '''
        Test states definition and Test state transition matrix.
    '''
    def __init__(self):
        self.vm_current_state = 0
        self.vm_volume_current_state = 0
        self.volume_current_state = 0
        self.image_current_state = 0
        self.sg_current_state = 0
        self.vip_current_state = 0
        self.sp_current_state = 0
        self.snapshot_live_cap = 0
        self.volume_vm_current_state = 0
...

一個階段可以被表示成一個整數(shù),即由這個階段的所有狀態(tài)的和。通過這個整數(shù),我們可以在轉(zhuǎn)換表中查找到下一個后選的操作。轉(zhuǎn)換表的一個例子如下:

#state transition table for vm_state, volume_state and image_state
    normal_action_transition_table = {
        Any: [ta.create_vm, ta.create_volume, ta.idel],
        2: [ta.stop_vm, ta.reboot_vm, ta.destroy_vm, ta.migrate_vm],
        3: [ta.start_vm, ta.destroy_vm, ta.create_image_from_volume, ta.create_data_vol_template_from_volume],
        4: [],
      211: [ta.delete_volume],
      222: [ta.attach_volume, ta.delete_volume],
      223: [ta.attach_volume, ta.delete_volume],
      224: [ta.delete_volume],
      232: [ta.attach_volume, ta.detach_volume, ta.delete_volume],
      233: [ta.attach_volume, ta.detach_volume, ta.delete_volume],
      234: [ta.delete_volume], 244: [ta.delete_volume], 321: [],
      332: [ta.detach_volume, ta.delete_volume],
      333: [ta.detach_volume, ta.delete_volume], 334: [],
      342: [ta.detach_volume, ta.delete_volume],
      343: [ta.detach_volume, ta.delete_volume], 344: [],
     3000: [ta.delete_image, ta.create_data_volume_from_image]
    }

通過這種方式,基于模型的測試系統(tǒng)可以保持運行,從一個階段到另一個階段,直到遇到預(yù)先定義的退出條件或者發(fā)現(xiàn)一些缺陷,它可以持續(xù)地跑很多天,數(shù)以萬次地調(diào)用API。

動作選擇策略

當(dāng)在階段間移動時,基于模型的測試系統(tǒng)需要決定下一個需要執(zhí)行的操作是什么。決定制定器被稱為動作選擇策略,一個可擴展插件的引擎,不同的選擇算法可以以不同的目的被實現(xiàn)。
當(dāng)前系統(tǒng)有三種策略:

  • 隨機調(diào)度器:最簡單的策略,為當(dāng)前的階段,從候選動作中隨機地選擇下一個操作。作為一種很直接的算法,隨機調(diào)度器可能會重復(fù)一項操作,而使得其他操作等待。為了緩解這個問題,我們?yōu)槊恳粋€操作都增加了一個權(quán)重,這樣測試人員可以為他們想多測試的操作賦予更高的權(quán)重。
  • 公平調(diào)度器:一種對待每個操作都完全平等的策略,以這樣一種方式補充隨機調(diào)度器:每個操作都有平等的機會被執(zhí)行,保證只要測試周期足夠長,每個操作都會被測試到。
  • 路徑覆蓋調(diào)度器:通過歷史數(shù)據(jù)決定下一步操作的策略。這個策略會記住已經(jīng)被執(zhí)行過的所有操作路徑,然后嘗試選擇一個可以形成新的操作路徑的操作。例如,給定候選操作A,B,C,D,如果前一個操作時B且路徑BA,BB,BC都已經(jīng)被執(zhí)行,策略將會選取D作為下一個操作,這樣路徑BD將會被測試到。

如上面提及到的,動作選擇策略是一個可擴展插件的引擎,每一個策略實際上由類ActionSelector派生來:
一個隨機調(diào)度器的實現(xiàn)例子像這樣:

class ActionSelector(object):
    def __init__(self, action_list, history_actions, priority_actions):
        self.history_actions = history_actions
        self.action_list = action_list
        self.priority_actions = priority_actions

    def select(self):
        '''
        New Action Selector need to implement own select() function.
        '''
        pass

    def get_action_list(self):
        return self.action_list

    def get_priority_actions(self):
        return self.priority_actions

    def get_history_actions(self):
        return self.history_actions

退出條件

在啟動基于模型的測試系統(tǒng)之前,退出條件必須被設(shè)定好,否則系統(tǒng)將會保持運行,直到一個缺陷被發(fā)現(xiàn),或者日志文件撐爆了測試機器的硬盤。退出條件可以是任何形式的,例如,在運行24小時后退出,在系統(tǒng)有100個EIP被創(chuàng)建后退出,在有2個停止的VM、8個運行中的VM時退出。一切都取決于測試者去定義條件,盡可能地增加發(fā)現(xiàn)缺陷的機會。

失敗回放

調(diào)試一個被基于模型的測試系統(tǒng)發(fā)現(xiàn)的失敗是很難而且令人沮喪的,大多數(shù)的失敗都由大量的操作序列暴露,而且它們通常缺乏邏輯并有著大量的日志。我們通常手動重現(xiàn)失敗,在痛苦地依照大約500,000行日志,使用zstack-cli調(diào)用API 200次后,我們最終意識到這個悲慘的任務(wù)不是人類可以做到的。然后我們發(fā)明了一個工具用于重現(xiàn)一個失敗,通過回放動作日志(純粹只記錄了關(guān)于API的測試信息)。
一個動作日志像這樣:

Robot Action: create_vm  
 Robot Action Result: create_vm; new VM: fc2c0221be72423ea303a522fd6570e9
 Robot Action: stop_vm; on VM: fc2c0221be72423ea303a522fd6570e9
 Robot Action: create_volume_snapshot; on Root Volume: fe839dcb305f471a852a1f5e21d4feda; on VM: fc2c0221be72423ea303a522fd6570e9
 Robot Action Result: create_volume_snapshot; new SP: 497ac6abaf984f5a825ae4fb2c585a88
 Robot Action: create_data_volume_template_from_volume; on Volume: fe839dcb305f471a852a1f5e21d4feda;  on VM: fc2c0221be72423ea303a522fd6570e9
 Robot Action Result: create_data_volume_template_from_volume; new DataVolume Image: fb23cdfce4b54072847a3cfe8ae45d35
 Robot Action: destroy_vm; on VM: fc2c0221be72423ea303a522fd6570e9
 Robot Action: create_data_volume_from_image; on Image: fb23cdfce4b54072847a3cfe8ae45d35
 Robot Action Result: create_data_volume_from_image; new Volume: 20dee895d68b428a88e5ec3d3ef634d8
 Robot Action: create_volume_snapshot; on Volume: 20dee895d68b428a88e5ec3d3ef634d8

測試人員可以通過調(diào)用回放工具重建失敗的環(huán)境:

robot_replay.py -f path_to_action_log

總結(jié)

在這篇文章中,我們引入了基于模型的測試系統(tǒng)。由于善于暴露邊界用例中的問題,基于模型的測試系統(tǒng)和集成測試系統(tǒng)、系統(tǒng)測試系統(tǒng)共同作為保衛(wèi)ZStack質(zhì)量的基礎(chǔ),使得我們可以以驕傲的自信發(fā)布產(chǎn)品。

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

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

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