Chatper 2 手工測試
測試是為了找bug的一系列過程.那么手工測試毫無疑問是主要活動
任何一個軟件公司發布的產品都有缺陷
這個缺陷是一種特殊缺陷,比其他任何缺陷都重要:即逃過所有各種檢測手段而最終存在于發布產品中的缺陷. 這些缺陷如何損害公司利益?用戶在使用軟件過程中找到這些缺陷。
觀點: 因為用戶是在使用軟件過程中找到這些缺陷,所以我們的測試人員也應該通過使用軟件來找到他們。
為什么被引入?
無論使用自動化測試和單元測試,還是其他一些手段,都難以接觸到這些特殊缺陷。
無論測試人員怎么實現自動化測試,即使全部都自動化,這些缺陷還是會處處作怪,并在產品中屢屢重現從而傷害最終用戶
手工測試的問題
缺乏目的性, 隨機性強:free testing , random testing, ad-hoc testing
重復性強,無聊透頂
新的手工測試方法:探索式測試
大家第一感覺會說探索式測試會不會和ad-hot testing / free testing / random testing 一樣啊。探索性測試有時與隨機測試混淆。隨機測試通常指即興的缺陷發現過程。從定義上理解,任何人都可以做隨機測試。
而探索性測試是基于feedback的,邊設計邊測試,有側重點的,高度依賴測試人員的背景,水平和經驗的一種測試技術。
軟件缺陷的預防
從開發出發
- 設計規范
- 代碼靜態分析工具
- code review
- unit test
上面的技術有問題,不解決問題,技術不會太有效
- 問題1. 開發找自己的代碼有效性差,不善于找缺陷,從編程角度出發
- 問題2. 代碼審核,靜態分析都是靜止狀態的技術。但很多缺陷,那些隱藏很深的缺陷,如果不在真實的運行環境中運行起來,不用真實的數據,是不會被觸發的
- 問題3. 缺乏數據。往往會出現這樣的情況,軟件運行一段時間后,當數據量積累到一定程度,軟件出現故障了。
測試是一個動態的過程,它包括在不同的環境中運行軟件,使用合理的測試數據,并在較短的測試周期內盡可能多的嘗試不同的輸入值。這就是軟件測試人員可以施展身手的地方。
軟件缺陷的檢測手段:自動化和手工
自動化的好與壞
- 好: 編寫一個簡單的程序,就可以執行無數次測試用例,還可以發現很多軟件缺陷??梢员欢啻沃貜褪褂?。
- 壞: 自動化代碼可靠?維護成本?“預言家難題”?自動化已經行之多年,為什么到用戶手里還是有問題?
預言家難題:測試中最艱巨的任務之一,就是在運行一個測試用例時,如何才能知道被測試軟件確實完成了它應該完成的任務?被測試軟件是否輸出了正確的結果?在運行過程中,是否帶來副作用?如何才能確信這一點?如果給定一個用戶環境,特定的數據配置和輸入順序,有沒有預言家能斷言:軟件確實做了,也只做了它所應該做的事情?,F實情況下,往往由于軟件的設計規格說明書并不完整,或者根本沒有,這導致軟件的測試人員也沒有辦法做這個斷言。所以說:自動化確實很重要,但光靠它還不夠,過度依賴自動化測試會為程序的最終成功帶來隱患。 如果測試人員不能依靠開發人員的缺陷預防工具和自動化手段,他們還能寄希望于什么呢?唯一答案是手工測試
手工測試
主觀能動性,設計真實的場景,找到顯而易見和難以察覺的缺陷。特別是對于業務邏輯相關的,比起自動化,手工測試是最理想選擇。但手工測試遠遠不能徹底解決問題。慢,沒有規律,不可反復使用,發現問題后也不能重現,不能移植,沒有可借鑒的經驗教訓,無聊透頂。
怎么辦?形勢嚴峻? - 使用手工測試領域最好的技術:探索式測試
探索式測試
手工測試使用腳本:執行 test case。死板。一定的變通?不指定test data,給一個籠統的用戶場景,有一定的發揮余地。測試腳本可以規定得很細,也可以含有一些粗線條的描述。當測試腳本比較籠統的時候,測試人員就需要學習隨機應變的本領,掌握面對各種選擇時如何可以進行合理的判斷,這就是探索性測試所要闡述的問題之一。
探索式測試:完全拋開測試腳本??瓷先]規律,但對于一個有經驗并熟練掌握探索式測試的人來說,就有效。
使用探索式測試并不是說不寫文檔。測試結果,測試實例,測試文檔都會在運行測試時創建。這與先編寫好完全不同
探索式測試最適用于敏捷開發過程的web應用程序。這些程序開發周期很短,基本沒有時間可以編寫正式的測試腳本。產品功能變化很快,原來的測試用例可能很快失效,那為什么寫?更多的時間用于實際測試而不是維護測試用例。
探索式測試的缺點:沒有重點,漫無目的,對某些功能重復測試,在多人測試團隊該情況就更嚴重了。木有文檔,怎么保證測試覆蓋率?
這里要強調指導方法的重要性了
- 局部探索式測試法,輔助測試人員在測試過程中及時作出決定。
- 全局探索式測試法,幫助測試人員設計整體測試計劃和測試策略
- 把探索式測試和使用腳本的手工測試合并起來,混合探索測試技術。使用正式腳本可以為探索式測試設立一個明確的框架范圍,探索式測試則可以提高腳本測試的有效性。
最好的是從正式腳本開始,然后再使用探索式測試法在腳本中加入各種各樣的變化。這樣做的話,單一的測試腳本會演化出很多探索式測試用例。
Chapter 3 局部探索式測試方法
任何一個錯綜復雜的缺陷都與功能無異.軟件測試的復雜性。測試軟件這種高度復雜的產品,如果不知道正確結果是什么,質量標準定義又很含糊不清,這活兒幾乎沒法干。
那么軟件測試人員面對這類 mission impossible時,我們要做什么?
- 欣賞軟件測試的高度復雜性和艱巨性。測試無涯,我們永遠也做不完,所以必須要把所有要做的事情按照優先級排序,然后從最重要的事情做起。我們的目標是軟件發布的時候可以達到如下的目標:所有重要的任務都完成了,而剩下沒做的事情都是比較次要的。如果我們做到這一點,就可以較早盡可能降低發布風險。
測試是一個不停進行抉擇的過程。局部探索式測試幫助你做出各種細小決定,比如如何決定文本框的輸入值,如何解釋錯誤消息,如何理解前一次輸入和后一次輸入值之間的關系。
根據軟件的各種屬性,我將決策分為5部分
- 輸入input
- 狀態state
- 代碼路徑 code path
- 用戶數據 user data
- 執行環境 execution environment
測試工作簡化為現在所有輸入或是運行環境的全體集合中選擇一個子集,然后再輸入時使用選中的子集,最后通過推理認定是否這些輸入已經足夠多了。
用戶輸入
輸入是由應用程序外部引發的,并導致應用程序執行某些代碼。
比如在text field輸入一些值不算輸入,必須點擊了button算輸入,因為這樣才能執行代碼
- 原子輸入:2,a
- 抽象輸入:月份 什么長度
其實更復雜:各種輸入間會互相影響。
比如機器上查找cd文件沒有問題,找視頻文件沒有問題,但同時找cd和視頻文件,就有可能報錯了. 各種輸入的順序:輸入a 輸入b ab ba aa bb
如何測試用戶輸入
合法輸入和非法輸入
非法輸入需更加重視,因為大部分dev不喜歡寫錯誤處理,而是喜歡寫實現功能的代碼。正因為這種態度,會導致應用程序有問題. 對于非法輸入,可以采用以下方式處理
- 錯誤處理 error handler
- 輸入篩選器 input filter. 把壞的輸入值擋在應用程序外. powerpoint的插入表選行列,dropdown list. 開發人員是否正確地實現了該功能。非法輸入和合法輸入的區分。比如月份加入了0
是否可以繞過屏蔽器。( 一個dropdown list option的value通過修改html源代碼來實現) 如果有某種方法可以讓輸入值進入系統,或者當輸入值進入系統后還可以修改,那么屏蔽器就沒有什么用處了。開發人員還要實現更多的錯誤檢查代碼。 - 輸入檢查 input check.
輸入檢查屬于應用程序代碼主線的一部分,通常通過if then else 或是switch case 等等實現。一般是報錯,顯示錯誤信息。該信息會描述當前狀況,并準確地表示當前的輸入值出現了不合法的情況. 牢牢抓住顯示的錯誤信息,必須閱讀每一條錯誤信息,是否寫錯?錯誤信息給出的原因如為什么錯,怎么修改會給測試人員更多的啟發 - 使用異常 exception. 異常處理代碼就像錯誤檢測,但是它不是對每一個輸入值進行檢測,異常處理檢測任何一種錯誤。如果在軟件運行的過程中出現任何被指定的錯誤,該段代碼也會進行處理。異常處理代碼處理各種各樣的軟件失效狀況,不單單局限于非法輸入. 所以說,由異常處理代碼產生的錯誤信息相對于由特定的輸入檢查產生的錯誤信息來說,一般更籠統,含糊。
反復測試同一段函數,繼續使用剛才引發異常的輸入數據,或是修改一下,看會不會出錯。或是運行其他一些要調用該函數的測試用例,看看會發生情況。
非法輸入應該被忽略,或者應該觸發出錯信息(出錯信息可以顯示在一個彈出式對話框中,可以被輸出到錯誤日志文件中,顯示在界面的某個特定的保留區域)。應該按照按照規格說明書定義的來正常處理合法的輸入,產生合理的結果。如果你觀察到的和上述情況有任何不同,這是軟件缺陷。
常規輸入和非常規輸入
特殊字符
默認輸入
開發人員設置的默認值。測試人員需要檢查這個默認值是否正確。比如一個打印表單中的打印頁數字段,默認值就可能是“全部”,我們需要需要驗證這是不是用戶最有可能使用的數值。看見默認值,可以把它刪除,留下一個空白的。開發人員往往沒有考慮到這個情況,因為他們把時間都花在如何設置默認值,卻沒有去想如果改數值沒有怎么辦。然后還可以試試默認值附近的值。比如給數字默認值大1小1,給字符串默認值頭部尾部添加修改刪除。一個字段如果被預先設置好默認值,便和不含默認值的字段在程序處理時往往有很大不同,花點時間,會有回報。
空白輸入
顯示出的錯誤信息還會根據哪些字段沒有填而變化
輸出來指導輸入
先明確自己希望軟件會產生什么樣的反應,然后確定哪些輸入會引發相應輸出,然后再測試中使用這些輸入值。
- 確定希望程序產生什么樣的輸出結果,然后考察所有的用戶場景,看看如何去生成期望的結果
- 先觀察輸出結果,然后再選擇新的輸入,并保證新的輸出是重新計算后的結果,或者確保新的輸出結果和原先不同
- 尋找被保存起來的輸出結果。這種輸出值通常被計算出來后會被顯示在屏幕上,或者被存儲在一個文件中以供程序未來再次讀取。如果這些值是可以改變的,一定要試試去改動這些值或者改動它們的功能(大小類型)
狀態
可以這么來看待軟件狀態,在我們選擇下一步使用哪個輸入時,必須考慮從前使用過的那些輸入所造成的累積效應。應用程序和其運行環境進行交互和接受到的所有輸入導致軟件狀態發生變化。
如果輸入a,它就會改變被測軟件的當前狀態,然后再輸入a,就不能算是運行完全相同的測試了。應用程序的狀態已經改變,所以對應于輸入a的輸出結果也可能完全不同。和輸入一樣,狀態同樣會影響軟件是否會失效。在一種狀態下輸入一個值,可能一切都很好,如果同樣的輸入值被使用于另一個狀態下,程序可能就出錯了。
如何測試軟件狀態
- 軟件接受到輸入,并在內部保存這些輸入值后,軟件狀態就會發生變化。測試人員要測試這些狀態變化的情況。
- 測試是否正確更新了它自身的當前狀態?
- 測試程序的當前狀態是否會導致接受某些輸入時發生故障?
- 測試軟件是不是進入了一些它不應該進入的狀態?
輸入和狀態之間的關系
-
使用狀態信息來幫助尋找相關的輸入
測試輸入的各種組合可以說是測試的一個基本常識。比如:某網站,它允許用戶輸入coupon來打折,但coupon不適用于打折商品。所以我們需要測試:shopping cart上加入打折商品,然后輸入coupon來進行測試。不僅僅是只測試coupon用于非打折商品。在測試中,狀態(購買的商品和價格)對結果有什么影響。加上輸入值coupon,就會得出輸入組合。
-
使用狀態信息來辨識重要的輸入序列
當一個輸入導致狀態信息被更新時,緊接著再多次使用同樣的輸入會導致一連串的狀態變化。如果狀態變化在某種方式上被累加起來,就必須考慮到溢出的問題了。比如shopping cart會不會被裝滿?通過觀察被測應用程序中狀態的累積程度,然后重復使用相同或不同的輸入來檢驗這種累積是否會帶來副作用。
代碼路徑
一條代碼路徑就是一連串代碼語句,它起始于軟件開始運行的語句,終止于一條特定的語句,往往就是那條代表軟件運行結束的語句。
量大。因為分支,循環等。
測試人員必須明確知道程序里可能有哪些分支,并理解哪些分支輸入會導致軟件走這條分支而不是另一條。這不是一個簡單的任務,特別是要求測試人員在不接觸源代碼或者不適用那些可以把輸入映射到代碼覆蓋率的工具時。如果某些代碼路徑沒有被測試到,他們中就很有可能存在軟件缺陷。 白盒測試可以做路徑覆蓋。 黑盒來說,更多的業務理解的深入,測試用例的設計,就是測試用例的覆蓋率上。
用戶數據
測試環境最好有和真實用戶數據相似的數據. 做到和真實用戶一樣的數據 很難:
- 真實用戶數據經過長年累月的添加修改,非常多。而測試只是幾天,幾星期的事情,測試必須在短時間內產生一下子這么多的數據
- 真實用戶數據通常包括測試人員不了解的很多相互關系和結構,測試人員也難以推測。
- 數據量大的時候,在哪里可以放得下這些數據也是個問題。不可能去買個昂貴的數據中心。
- 用真實的數據庫?那么必須非常小心,且做一些額外工作把數據庫恢復到原先的狀態?;蚴菑椭朴酶北尽?/li>
- 隱私。
不管有沒有用戶的真實數據,測試人員都很麻煩。。。。
運行環境
退一萬步說,即使我們可以測試隨著用戶輸入,狀態變遷,用戶數據變化而變化的所有代碼路徑,當軟件被安裝到一個嶄新的環境中,還可能失效。環境本身就是一個輸入源。
運行環境包含什么?很難回答?os,各種配置,網絡情況,其他程序等等很難測試。各種各樣的變化,變化的絕對數量遠遠超過我們可以全部嘗試的范圍。
小結
輸入,代碼路徑,狀態,被存儲的數據和運行環境等,這里有太多太多的因素,而每種因素又有太多的變化可能,這一切使得軟件測試變得極其復雜。無論你是采用寫好case或是探索式測試來解決這個無窮變化的問題,都可以說難如登天。
探索式測試試圖把制定計劃,進行測試,重新制定計劃等多個過程有機地結合起來,每次只前進一小步,但這每一步都是由軟件過去和當前的運行狀況,軟件在測試時表現出來的各種行為和軟件運行時留下的種種蛛絲馬跡來即時確定的。有效地利用探索式測試技術可以幫助我們發布一個高質量的軟件產品。
Chapter 4 全局探索式測試方法
商業區(軟件重要特性)測試類型
- 指南測試法:根據用戶手冊或是產品說明書,驗證手冊描述的各種特征的正確性以及用戶手冊的準確性
- 賣點測試法:軟件最能賣錢的特性,最重要的。
- 地標測試法:軟件特性為地標,在選擇完地標后,需要確定它們的前后順序,然后從一個地標執行到另一個地標來探索應用程序,直到訪問了列表中的所有地標??梢杂械貥烁采w圖來表示工作的進展
- 極限測試法:向軟件提出很多難以回答的問題,找麻煩
- 快遞測試法:專注于數據的流向。應該確認那些被存儲起來的輸入數據并跟隨它們走遍軟件
- 深夜測試法:測試除執行賣點特性的代碼,比如各種維護任務。
- 遍歷測試法:通過選定一個目標,然后使用可以發現的最短路勁來訪問目標包含的所有對象
歷史區(老的功能和缺陷修復代碼)測試類型
- 惡鄰測試法:反復測試產品缺陷多的地方,對鄰近功能采用遍歷測試法,以此來驗證那些修復已知缺陷的代碼沒有引入新的缺陷
- 博物館測試法:測試遺留代碼
- 上一版測試法:如果當前產品構造是對先前版本的更新,很重要的一點就是必須運行先前版本上支持的所有場景和測試用例
娛樂區(輔助特性)測試類型
- 配角測試法:專注于某些特定的特性,它們雖然不是那種我們希望用戶使用的主要特性,但和那些主要的特性一同出現在顯示器上
- 深巷測試法:如果測試部門已經跟蹤產品特性的使用情況,深巷測試法就是建議測試人員應該測試使用情況列表中排在最下面的幾項特性。測試各種特性之間的互相作用,試著把最流行和最不流行的特性放在一起混著測。
- 通宵測試法:讓程序一直運行
旅游區(快速訪問軟件的各個功能)測試類型:
- 收藏家測試法:收集軟件的輸出,確保能觀察到軟件能生成的任何一個輸出
- 長路徑測試法:測試離應用程序開始點盡可能遠的特性
- 超模測試法:關注GUI
- 測一送一測試法:測試同時運行同一應用程序多個拷貝的情況
- 蘇格蘭酒吧測試法:有些地方只能從別人那里獲得信息,然后測試,測試人員需要事先知道如何去找到它們。
旅館區(忽視的次要功能)測試類型:
- 取消測試法:啟動操作停止它。使用cancel按鈕。使用取消功能
- 懶漢測試法:測試人員做盡量少的工作,接受所有的默認值,保持輸入字段為零,盡量少的操作。
破舊區測試類型:
- 破壞者測試法:破壞文件,限制資源等,讓運行環境惡劣。
- 反叛測試法:非法輸入,輸入不可能的數據,惡意輸入
- 強迫癥測試法:重復操作。改變順序
Chapter 5 混合探索式測試技術
探索式測試蘊含著豐富的策略,它將結構化的思想和自由的探索方式很好地結合起來,在發現缺陷以及檢驗正確性上都卓有成效
基于場景的探索性測試
測試場景描述了基本的功能,探索則增加了盡量多的變化。
場景可以代表探索式測試的一個絕佳的起點,探索可以給場景加入寶貴的變化,否則場景將很有限
場景的來源
- 需求說明書
- 設計開發過程中信息收集
- 敏捷模式的用戶故事
場景的分類
- 講述用戶故事
- 描述需求
- 演示產品功能
- 演示集成場景
- 描述設置和安裝
- 描述警告和出錯情形
上面的分類都是可以作為開展探索性測試的場景
使用基于場景的探索性測試技術
場景操作:對場景的步驟加以操作,來給場景注入變化。改變特定動作的場景,并從場景中派生出衍生場景,用于測試不同的狀態和不同代碼路徑
-
插入步驟:給場景增加額外的步驟可以使他們更加多樣化,從而測試更多的功能
- 增加更多的測試數據:這個場景用到哪些數據?怎樣有意義地增加測試所使用的數據?
- 使用附加操作:哪些其他輸入和現有場景使用的輸入有關?
- 訪問新的界面:其他的哪些界面和現有場景使用的界面相關?
這些步驟最終都需要測試人員返回到原始場景。我們的目的是加強場景而不是徹底改變場景的基本目的。在這類場景操作中,測試人員通過增加其他輸入,加大數據量或變化場景把整個場景拖長,但是并沒有改變場景的核心目標。
刪除步驟:遞進的方式重復應用這個場景操作,每次只刪除一個步驟。去掉冗余和可選的步驟,使場景步驟盡可能的少??梢杂脕頊y試應用程序是否可以識別出現在缺少信息或者缺乏一次從屬功能
替換步驟:如果場景中某些步驟可以有多種方法完成,就可以用替換步驟的場景操作來修改這個場景
重復步驟:場景經常包含非常明確的動作順序。通過重復單獨的步驟或重復一組步驟來改變這個順序。測試人員的任務是理解這些變化并創建適當的重復順序
替換數據:測試人員需要知道與應用程序相關的數據源并創建各種各樣的變化
替換環境:兼容性,什么瀏覽器,系統有變化,配置變化,cookie變化
漫游測試:測試人員查看腳本,找到需要測試人員做決定的地方或者找到可能產生邏輯分支的地方,先往完全不同的方向走,然后再返回到腳本描述的主要路徑。漫游實際上創建出相當長的和范圍更廣的衍生場景。
賣點測試法:現有場景加入新主要功能,測試功能間的互動。
地標測試法:從場景開始并從場景中選取特定功能的地標,然后隨機打亂這些地標的順序,這樣得的到場景就和原始場景不同了。
極限測試法:挑戰軟件,向它提出困難的問題
深巷測試法:現有場景加入新的不重要的功能
強迫癥測試法:重復場景中的每個步驟兩次或三次。場景中任何操作數據的步驟都值得我們重復做,因為它會導致軟件處理內部數據,設置內部狀態,然后再改變內部狀態。在軟件中四處移動數據歷來是有效的測試方法,它可以幫助我們找到重要缺陷。
通宵測試法:不斷重復運行場景而不需要退出被測應用程序
破壞測試法:在運行場景測試時,在資源調用處進行破壞活動。
收藏家測試法:執行場景和衍生場景時用文檔記錄下所觀測到的每個輸出,輸出越多場景越多
超模測試法:關注界面
配角測試法:測試人員不是執行腳本描述的功能,而是找到最近的鄰近功能來執行。
取消測試法:所有功能的取消
chapter 6 實踐中的探索式測試
探索式測試用于幫我們在測試設計中開發出測試用例,它也可以幫助我們發現在規范說明書中可能漏掉的用戶場景,還可以組織測試人員的測試思路
實踐中有用的漫游測試方法
出租車測試法
對于要到達所需的屏幕、對話框或一些其他功能組件,用戶通常有大量的路線可以選擇。因此,測試人員的責任和出租車司機一樣,他們必須熟悉到達指定位置的每天可能的路勁。在某些情況下,目的地狀態有可能依賴于路勁而發生一些變化,這種情況亦應進行驗證。這個漫游是從強迫癥測試法派生出來的,該測試方法的最終目的是要重復執行某項特定的操作。但是,不是重復執行完全相同的測試路勁,重點是要執行不同的測試路勁。這是它和強迫癥測試法的關鍵區別。
出租車禁區測試法
這種漫游的目的是要驗證無論選擇哪一條路用戶始終都無法到達目的地。
取消測試法
- 集中測試應用程序在功能終止后是否能繼續正確工作。
- 在取消被測對象之前應該改變它的狀態
- 在進行取消操作后再次嘗試同一場景也非常重要
破壞測試法
迫使你考慮應用程序所使用的資源,已便你可以調整可用資源,從而發現會導致其失敗的場景
快遞測試法
專注于數據。修改屬性和項目,并確保它們在其他地方被正確刷新。幫助我們確定這些類型的數據依賴關系,并指導我們有條理地思考數據元素之間的關聯。所以作為測試人員,我們需要思考一個數據在不同模塊之間的傳遞關系以及數據被更改后再各個模塊
測一送一測試法
可以發現應用程序在多用戶同時使用時的缺陷
不同的漫游測試法可以用于開發周期的不同階段
早期開發階段的目標
- 早期發現設計缺陷
- 發現被誤用的設計
- 發現用戶界面和可用性上的誤用
這些目標讓漫游變得更有目的性:這些探索式測試注重于把事情做成而不是以某種特殊的方式做事情。這些漫游法包括地標測試法和出租車測試法。在測試周期的早期階段,測試工作會側重于發現大的問題
后期開發階段的目標
- 確保產品功能的正常工作
- 確保用戶數據的安全性
- 確保完工的軟件符合要求
- 描述功能特征的適用范圍
- 確認以前的缺陷不再重現
這些目標讓漫游變得詳細而精確:這些探索式測試注重于一種特定方式來探索某一特定的東西。這些漫游法包括深巷測試法、遍歷測試法、超模測試法和取消測試法。
Chapter 7
漫游與測試中有五個棘手問題
-
漫無目的 aimlessness
- 有計劃,有準備,有策略和有多變的戰術,這是成功進行軟件測試的前提。擁有測試策略和規范的技術使得測試人員在處理他們的任務時目標更明確
-
重復性 repetitiveness
- 漫游路徑又迫使測試用例的產生具有更多的變化
暫時性 transiency
單調性 monotony
健忘性 memorylessness
其他
探索式測試是一種強調個人自由與責任的測試方法,讓獨立測試人員可以借由不斷的學習來改善測試的規劃與測試的執行,而在測試過程中也會同時改善測試案例達到相鋪相成的效果