生活中有這麼一種現象:若是你關注某些東西,它就會常常出如今你眼前,例如一個不出名的歌手的名字,一種動物的卡通形象,某個很是專業的術語,等等等等。這種現象也叫作「孕婦效應」。還有相似的一種效應叫作「視網膜效應」,它講的是:你有什麼東西或者特質你就特別容易在別處發現你有的這類東西和特質。幹了多年測試的我就會常常發現平常使用的系統中有不少的bug,而我老婆就發現不了。今天要說的事兒是「重現難以重現的bug」,這件事兒在本週共碰見了4次:第一次是微博上有一篇《程序員,你調試過的最難的 Bug 是?》(後面會附上);第二次是一個同事跟我抱怨,好幾個bug難以重現特心煩,並問我怎麼處理比較好;第三次是本週上線的產品出現了一個當時難以重現的bug,咱們對它作了初步的分析;第四次是翻看史亮寫的書《軟件測試實戰》,偶爾翻了翻,居然看到一小節在寫「處理難以處理的缺陷」。這時候,腦子裏不少東西聚集到了一塊兒,我想仍是記錄一下吧。下面是正文:html
也許測試人員(尤爲是對新手來講)在工做過程當中最不肯遇到的一件事情就是:在測試過程當中發現了一個問題,以爲是bug,再試的時候又正常了。碰到這樣的事情,職業素養和測試人員長期養成的死磕的習性會讓她們以爲不能放過這個bug,可是重現這樣的bug有時候須要花費大量的時間,有的時候還有一些盲目性(由於黑盒測試的緣故,不少內部狀態是不可見的,所以沒法獲取有效的信息來作跟蹤),效率較爲低下。在實際工做中,時間和進度擺在那裏,在經歷了屢次痛苦的失敗嘗試以後,測試人員的處理方法通常會有以下幾種:1.向開發人員尋求幫助來重現bug;2.當作一個issue報給開發人員。但是這樣的作法存在以下問題:linux
1.開發人員責任心不夠強,不肯意花太多精力去求證這件事情,常見的回覆就是:在我這兒沒事兒啊,我也重現不了,bug關了吧。結果隨後在生產系統上,bug又開始sui隨機出現了。程序員
2.就跟測試人員不擅長編碼和調試同樣,開發人員並不擅長找出bug。通過一番嘗試之後,他們也找不出什麼問題來,常見的回覆同第一條是同樣的。bug上線後又出現的宿命也是同樣的。數據庫
這時候,真正的問題來了:如何捕捉難以重現的bug?這件事兒對於測試人員來講就這麼難麼?windows
答案並不那麼樂觀,重現「難以」重現的bug原本就是一件「難以」完成的事情。但「難以」並非不可能,經過一系列的測試、分析方法,咱們是可以抽絲剝繭把絕大部分隱藏的很深的bug揪出來的,固然有的時候你要考慮投入產出比,但投入產出比不是本篇要考慮的,本篇只講一些我積累的經驗。緩存
爲何不能重現bug?安全
最大的緣由就是:測試人員對被測物的瞭解還不夠深刻。服務器
我曾經作過一段很長時間的收集和統計,那些被稱做過「難以重現」的bug最後均可以分爲以下幾類:網絡
1.環境的變動形成了bug難以重現,這裏的環境包括了:基礎軟硬件環境(操做系統、網絡、存儲、中間件、容器等等),被測物自身發生了某些變動。環境的變動通常是因爲多人共用環境形成的,也有少許狀況下是系統內部或者時間觸發的變動(這類bug很是難重現)。多線程
2.沒有找到真正引起bug的操做。這些操做每每是一些不怎麼顯而易見的操做,測試人員在不經意間完成,而又忽略了這一操做,以至難於重現bug。
3.沒有找到真正會引起bug的操做序列。不少bug的重現須要知足多個條件。在知足多個條件的狀態下,你作了對應的操做,bug纔會被觸發。
4.bug必須使用特殊的數據纔會出現,測試人員沒有意識到她使用的數據的特殊性。一種比較難搞的狀況是:同一組輸入,在不一樣狀況下(不是不一樣的業務狀況)輸入會被轉化成不一樣的數據。我曾經見到過這麼個例子,程序員用系統當前時間做爲隨機數的種子來生成id,可是id設置的比較短,一個存儲的操做使用這個id,在一些狀況下,發生了衝突,當時作功能測試這種小几率事件耗費了測試人員大量時間也沒有穩定重現,仍是在性能測試的階段測試了出來。
5.測試人員因爲錯誤操做,出現了誤報(這很常見)。好比,記得本身執行了step3,其實沒有,或者沒有正確執行step卻以爲正確執行了。
怎樣對付這樣的bug呢?
我喜歡James Bach 說的那句話:測試就像CSI。CSI是Criminal Scene Investigation 的縮寫,也是我很是喜歡的美國系列劇。
從我來看CSI的精髓在於:仔細觀察,詳細記錄,科學分析,嚴密推理,有序求證,大膽假設,持續不懈,團隊協做和一點兒運氣。找bug其實和CSI探員作犯罪現場調查沒什麼太大區別。得知道,你工做的重要性一點兒不亞於CSI探員。
仔細觀察:第一件事情就是要觀察,觀察你所作的一切操做和一切相關的系統反饋。在一開始,觀察的重要性要遠遠大於思考,經過觀察你得到蛛絲馬跡,這些蛛絲馬跡是你進行思考和假設的關鍵輸入。例如,我在一次測試的過程當中,發現作某種操做的時候會至關慢,極少數狀況下還報錯過一兩次,當詢問了開發人員後得知這個操做的後臺實現步驟是:先查看數據是否在緩存中,若是不在,則從遠端服務器請求數據。我抓住少數狀況下會報錯的這一現象,仔細觀察它的出錯信息後猜想報錯並非由於網絡鏈接不穩定引發的,而是因爲遠端服務接口實現有問題引發的,後來從新設計了測試用例,果真找到了問題所在。若是不仔細觀察出錯信息,就會聽信開發人員認爲這是網絡不穩定引起的正常issue而錯過這個bug。
詳細記錄:詳細記錄你的操做步驟及返回結果很是有助於回朔問題,也有助於後續分析。準備一個word文檔,和截圖工具備時候很是必要。另外,在觀察的時候,你不只要注意被測物的最終返回,還須要觀察過程當中的一些中間狀態,每每這些中間狀態提供的信息纔是解開問題的關鍵。這些中間狀態通常會被記錄在log文件中,所以知道你的被測物是如何記log的,log被記錄在哪裏很是重要。log給了你另一個看系統的角度。log是有級別的,若是級別能夠動態調整,在找比較難找的bug時,能夠將log記錄的級別調至最低(DEBUG級)讓它們記錄更多內容。利用系統的錯誤轉儲文件(好比linux的core dump文件,windows下也有相應的記錄轉儲文件的方式)分析也是個不錯的辦法(須要較高技術能力),但通常建議測試人員把這些轉儲文件交給更專業的開發人員來分析。在我短暫的C++開發歲月中,有使用過GDB閱讀轉儲文件的經歷,那絕對不是愉快的回憶。你瞧,測試人員的主要工做是找出可重現的bug,並非定位它們,不是麼?
除了log,若是能有監控信息,也要查看他們。好比系統提供的監控平臺,監控日誌;應用本身的監控平臺、監控日誌(若是有的話);採用一些外部技術手段截取一些中間狀態信息,如使用sniffer抓取通信包,使用Fiddler截獲HTTP報文內容;給運行程序插樁來查看內存,堆棧,線程,函數被調用狀況等狀況,如Jprofile,gpertool等等。
科學分析:對於黑盒測試人員來講,科學分析意味着你須要有必定的分析策略。咱們須要採起一些形式化的方法來完成咱們的分析。基於個人統計,缺陷難以重現有很大一部分緣由是由於「沒有找到真正引起bug的操做序列「。測試人員不可能像開發人員那樣去跟入到代碼內部,設置斷點調試程序,他們主要的測試方式是直接來操縱被測物,並從外部觀察被測物狀態的改變。顯而易見,「狀態轉換圖分析法」是測試人員對付「難以重現bug」的最強有力武器之一。狀態轉化圖可以幫助測試人員很好的選擇操做路徑,而且知道這麼作有什麼意義。「狀態圖轉化法」絕對是測試人員值得花時間學習和研究的一種方法,你能夠走的很深,也能夠研究得很遠(能夠從MBT的方向進行拓展),限於篇幅,這裏就不展開了。在這裏推薦《探索吧!深刻理解探索式軟件測試》這本書,它的第八章對「狀態轉換」作了很是實用的描述。
上文分析的讓bug難於重現的另外一種緣由是沒有找到「真正引起bug的特殊數據」。個人經常使用作法是這樣的:1.畫出系統交互圖(要真正弄清系統的邊界,這很重要),並識別出每種交互會有什麼樣的輸入、輸出數據和中間數據,識別出這些數據的規約和格式,這樣你就不會對數據有遺漏。2.考慮數據的等價類、邊界值,對這些輸入進行組合,分析數據之間是否有耦合關係,若是有耦合關係,弄明白關係是什麼,設計違背這些關係的用例,最後採用組合測試工具初步生成測試集,再人工選取最可能出問題的數據集(直覺有時候很是管用)。
嚴密推理:天馬行空對測試人員很重要,可是當你試圖重現一個bug的時候,這並非一個很是好的方法。抓住了蛛絲馬跡,你就要推理是爲何產生了這種蛛絲馬跡。限於工做性質,測試人員更多的會從:業務完整性、數據完整性、業務正確性、數據正確性等方面考慮問題。可是,若是測試人員對被測物的IT架構有比較深刻了解的話,推理的範圍會擴大到技術實現層面,如:多線程可能引起的問題,網絡引起的問題,excepiton處理不當引起的問題,全局事務設計不當引起的問題,內存泄漏引起的問題,數據庫表設計不合規引起的問題等等等等,這些會讓你的分析推理能力如虎添翼。固然,若是限於條件,測試人員不具有這類能力,則應該在適當的時候請求開發人員協助。
有序求證:這裏只有一點須要注意。那就是,在求證的過程當中不要打散彈槍,按照你的推理一步一步的來,一個個推理的來驗證,一次只引入一處修改。這樣才能讓你的捕蟲網編制的足夠細密。
大膽假設:有的時候,看似八竿子打不着的東西居然存在着千絲萬縷的聯繫,而你獲取信息的過程老是一個由少及多的過程,一開始這些聯繫是沒法被識別出來的。經過推理,逐步驗證,你慢慢的識別出了大部份內在聯繫。但有時候這種逐步推動的工做也會有侷限性,工做若是出現了瓶頸(你試遍了你全部的假設,都沒有重現bug),這時候就須要發揮一點兒想象力了,天馬行空這時候從必定程度上又變得有用起來,固然天馬行空也不是無厘頭,還得靠咱們所謂的「靈光一閃」,這號稱是潛意識在幫助你。CSI的劇情中不也老是出現這種柳暗花明的橋段麼?
堅持不懈:話很少說,有的時候你差的就是那麼一點兒點兒耐心。
團隊協做:不少狀況下,重現bug不是一我的能搞定的。咱們須要測試環境ready,測試數據ready,各類監控、分析工具ready,各類不一樣的視角開拓思路、加深對被測試物的認識。這是team work!!!獨行俠有時候很管用,可是終究有極限。這就是爲何CSI是一票人在作而不是一兩我的在作。
一點兒運氣:說實在的,有的時候重現bug就是靠運氣,你不得不認可這一點。事實上不少美好的事情發生都得依靠運氣,好比中彩票。要記住的一點是,運氣是創建在你不懈努力的基礎上的。若是你一張彩票不買,你確定什麼也中不了。但若是你堅持買上幾年,中個五塊十塊甚至二百也不是夢。
Let it go:全試過了,連運氣都沒有。你只能放手,回到最上面我說的那兩條了:找開發來幫忙,或者給開發報issue。btw,即便不能重現bug,也應該給開發人員提供更多信息:如log、dump文件、監控記錄、屏幕截圖等。作一個負責人的測試人員,把煩惱真實的留給下家,這很重要:)
最後給出一個軟件調試大牛 David.A.Agans對於軟件調試的九條建議,很是值得一讀:
http://sydney.edu.au/engineering/aeromech/MTRX2700/Course%20Material/lectures/PDF/week04/Debugging.pdf
9月25日:今天學到了一個詞:Heisenbug ,這詞的中文意思能夠被翻譯爲「神出鬼沒的bug」。。。這個單詞和量子力學元勳海森堡的名字差了一個字(Heisenburg) 量子力學的一個經典定理就是"測不許原理"。 你們的吐槽能力真強。 想了解細節請看下面的這篇文章:
http://blog.csdn.net/kjfcpua/article/details/8125306
9月26日:以爲有一些實例會更容易理解,我會盡可能收集一些例子放到這個帖子裏:
12月3日更新: 一個Ajax同步異步引起的血案 一個hotfix的debug過程:)
12月3日:其實上面的大段論述是站在測試人員角度上來看的。我也寫不少代碼,做爲一個開發人員(哈哈)我查錯的方式通常是:
1.先能穩定的復現錯誤。(這一步最難)
2.設樁,打印出一些中間狀態來分析,看到那一起錯了。
3.摘除不相干的代碼,慢慢迭代,定位錯誤。
4.若是發現調用第三方依賴跟你想的不同,先讀文檔,而後再測試第三方依賴。有問題再想辦法。
5.良好的程序設計和單元測試覆蓋度纔是不二法門,會讓你的調試變得簡化的太多。。。用過都說好:)
6.測試工做的確加強了我排錯的能力。coding的同窗都嘗試作幾個月測試吧。會有至關大的好處。
14年12月15日:早上的一個同事提出了一個小技巧,以爲很不錯,記錄一下:若是你沒有辦法穩定的復現bug,能夠經過幾率統計的方式將問題反饋。若是10次裏出現一次問題,不足以打動開發人員重視問題,能夠經過自動化測試的方式提升執行次數的數量級,若是一千次出現50次~100次。這個問題就足夠引發重視了。在negotiate的時候,這是一種好思路。
----------分割線,下面是轉載的文章--------------------------------