
所屬文章系列及序號(hào):尋找塵封的銀彈:bug修理廠(一)
看似沒有什么營養(yǎng)的bug描述里,其實(shí)蘊(yùn)含著兩個(gè)關(guān)鍵要素,往往被我們忽略。開發(fā)者一旦掌握了這兩個(gè)要素,靈感會(huì)被瞬間激發(fā)出來,立刻想到解決方案,而且是正確的方案!筆者之所以要在這里強(qiáng)調(diào)“正確的方案”,原因就在下邊對(duì)這兩個(gè)要素的講述之中。
【一個(gè)要素是條件】
Bug描述里一般會(huì)列出這些條件:操作系統(tǒng)的版本號(hào),環(huán)境變量配置了什么值(這些變量也許會(huì)是一個(gè)長長的列表),發(fā)生bug之前做了什么操作(這些操作也許會(huì)是一個(gè)長長的列表)。
我們需要從這個(gè)描述中找出導(dǎo)致bug的“條件要素”,才能更好的理解bug發(fā)生的原因,從而可以找到解決方案。這里提到的“條件要素”是指,如果不執(zhí)行某個(gè)測試步驟,就不會(huì)發(fā)生這個(gè)bug,那么這個(gè)步驟就是“條件要素”。
對(duì)于能夠100%重現(xiàn)bug(或叫必現(xiàn)),我們既可以用下邊的方法去找“條件要素”,又可以直接用設(shè)置斷點(diǎn)的方式去發(fā)現(xiàn)代碼錯(cuò)誤,不過設(shè)置哪個(gè)斷點(diǎn)也有很多可討論的地方,這些復(fù)雜的討論需要在開發(fā)者“做bug重現(xiàn)”時(shí)再去解決(參見后續(xù)文章)。在這兩種方法的具體選擇上,要看哪個(gè)方法更快、自己更熟悉等因素。
對(duì)于重現(xiàn)概率較低的bug,作為一個(gè)開發(fā)者,如何在這些海量信息面前,迅速找出那個(gè)決定性的條件?這絕對(duì)是個(gè)能力,而且有時(shí)很像智商測試題。這是程序員必備的技能,具體的方法有很多,其中一個(gè)方法是“對(duì)比”。
現(xiàn)代版的俗話說:“沒有對(duì)比就沒有傷害”。
當(dāng)開發(fā)者或測試者修改了某一個(gè)測試步驟,例如改變了一個(gè)環(huán)境變量的值、減少了一個(gè)測試步驟、改變了兩個(gè)測試步驟的順序,等等,有時(shí)就會(huì)發(fā)現(xiàn)bug沒有了,那就證明是這次的測試步驟改變導(dǎo)致bug消失的,條件要素也就找到了。
這里有兩個(gè)變化需要注意:
一、無論是必現(xiàn)的bug還是不必現(xiàn)的bug,修改一個(gè)步驟后bug消失了,一般情況下就能判斷出是這次修改步驟導(dǎo)致的,但僅限于一般情況,還有其他情況導(dǎo)致bug發(fā)生,例如多線程,這些復(fù)雜的討論需要在開發(fā)者“做bug重現(xiàn)”時(shí)再去解決(參見后續(xù)文章),這個(gè)階段只是為了讓項(xiàng)目經(jīng)理、開發(fā)者、測試者看懂bug就足夠做決策用了。
二、一次只能改變一個(gè)測試步驟,否則我們就無法準(zhǔn)確知道是哪個(gè)改變導(dǎo)致的。
到此為止,方法是有了,但是方法還不夠有力。一旦測試步驟非常多,那么需要對(duì)比的測試用例就多,有時(shí)會(huì)達(dá)到100條以上,這樣的效率就太低了,而且操作起來讓人心煩,根本找不到工作中的快樂。
好在我們還有更好的方法:谷歌搜索的“手氣不錯(cuò)”!也就是說,在把所有情況羅列出來以前,先用自己的直覺試驗(yàn)一個(gè)最有可能的用例,往往就能找到那個(gè)關(guān)鍵的條件要素。不過直覺需要知識(shí)和經(jīng)驗(yàn)的積累,更需要對(duì)經(jīng)驗(yàn)的“反芻”。
【另一個(gè)要素是“錯(cuò)誤是什么”】
有人會(huì)說,“錯(cuò)誤是什么”不是明擺著的嗎?看完下邊這個(gè)例子,他就能體會(huì)到“明擺著”只是一種奢望:
有一個(gè)Bug的標(biāo)題是:用戶打開某個(gè)界面時(shí),得到的人員列表不符合預(yù)期。
Bug的細(xì)節(jié)描述是:當(dāng)用戶打開應(yīng)用程序的一個(gè)界面時(shí),得到了一個(gè)如下這個(gè)不符合預(yù)期的人員列表:
002 B先生
003 C小姐
001 A先生
而期望值是:
001 A先生
002 B先生
003 C小姐
理解錯(cuò)誤的壞結(jié)果
如果這個(gè)列表比較長,那么靠人眼去對(duì)比期望值和實(shí)際結(jié)果,需要多花一點(diǎn)時(shí)間。不要小看多花的這一點(diǎn)時(shí)間,一方面,人的大腦看到瑣碎的東西一定會(huì)比較累。
另一方面,如果是產(chǎn)品經(jīng)理或項(xiàng)目經(jīng)理在看這個(gè)列表,他不可能去看每一個(gè)bug的細(xì)節(jié),因?yàn)樗枰驹谝粋€(gè)更高的視角去看待整個(gè)項(xiàng)目。所以他看到這種模糊不清的問題,要么多花一些時(shí)間去仔細(xì)理解這個(gè)bug,要么是認(rèn)為自己理解清楚了,從而讓這個(gè)bug在整個(gè)開發(fā)鏈條中的多個(gè)人之間流轉(zhuǎn)一圈,最后bug仍然沒有解決,這樣一來,多花的時(shí)間就有些可觀了。
導(dǎo)致理解錯(cuò)誤的淺層原因
下面就是一個(gè)導(dǎo)致bug無效流轉(zhuǎn)的“坑”。
開發(fā)者看到這個(gè)結(jié)果對(duì)比的直覺是:一定是第一列的顯示順序錯(cuò)了。之后,開發(fā)者就去改代碼,改完之后,測試者又發(fā)現(xiàn)了另外一個(gè)bug:
當(dāng)用戶打開應(yīng)用程序的一個(gè)界面時(shí),得到了一個(gè)如下列表:
001 A先生
002 B先生
003 C小姐
004 A小姐
而期望值是:
001 A先生
004 A小姐
002 B先生
003 C小姐
實(shí)際上只是因?yàn)樵谶@個(gè)列表中又加入一條數(shù)據(jù)而已,卻觸發(fā)了開發(fā)者改上一個(gè)bug時(shí)的理解錯(cuò)誤,導(dǎo)致修改bug的成本翻倍。
經(jīng)過分析,終于得出結(jié)論:應(yīng)該按照第二列排序,而不是第一列!
這還只是一個(gè)最簡單的例子,大部分人還是能一眼就能看出來,但是,實(shí)際的工作中遠(yuǎn)比這個(gè)情況復(fù)雜,想不“中招”就需要去蕪取精的能力了。
正確的做法是:
開發(fā)者看到這個(gè)問題,如果邏輯清晰的話,應(yīng)該能想到有很多種情況導(dǎo)致這種錯(cuò)誤,上文提到的只是其中的兩種情況,他想到之后要去找測試者確認(rèn),有時(shí)甚至要找到需求文檔編寫者和產(chǎn)品經(jīng)理。
導(dǎo)致理解錯(cuò)誤的深層原因
表面上看起來這是個(gè)溝通問題,其實(shí)不是,這是因?yàn)椋簻y試者確切地知道需求是什么,只是他認(rèn)為沒必要寫出來,而開發(fā)者也認(rèn)為自己理解對(duì)了,也就沒必要再問,導(dǎo)致了bug的無效流轉(zhuǎn)。當(dāng)大家都認(rèn)為沒有分歧時(shí),自然就不需要溝通,所以邏輯能力的欠缺才是深層原因!
解決邏輯能力欠缺的有效方法有兩個(gè):長期來看,應(yīng)該去學(xué)習(xí)、思考。短期來看,用最準(zhǔn)確的語言去描述,例如上文的bug描述應(yīng)改為:用戶打開某個(gè)界面時(shí),得到的人員列表應(yīng)該按照第二列排序。
導(dǎo)致理解錯(cuò)誤的知識(shí)原因
行文至此,如果讀者認(rèn)為這就是“第二個(gè)要素:錯(cuò)誤是什么”的全部了,“也不過如此嘛”,那有時(shí)還會(huì)掉到下邊這個(gè)更深的“坑”里:
對(duì)于使用分布式數(shù)據(jù)庫的系統(tǒng),程序中傳給數(shù)據(jù)庫的SQL中,如果沒有包含order by,那得到數(shù)據(jù)的順序是不會(huì)得到保證的!因?yàn)榉植际綌?shù)據(jù)庫為了提高性能,是用并發(fā)的方式從多個(gè)數(shù)據(jù)庫節(jié)點(diǎn)獲得數(shù)據(jù),既然是并發(fā),那就無法確定哪個(gè)節(jié)點(diǎn)的數(shù)據(jù)先傳到。
如果開發(fā)者沒有這個(gè)知識(shí),或即便有這個(gè)知識(shí)而忘記了在這里運(yùn)用它,就會(huì)掉到上邊這個(gè)“深坑”里。結(jié)果就是:讓一個(gè)bug衍生出另外一個(gè)bug,再衍生出第三個(gè)bug,讓這些“臭蟲”在開發(fā)者、測試者、項(xiàng)目經(jīng)理、產(chǎn)品經(jīng)理,甚至客戶那里轉(zhuǎn)啊轉(zhuǎn),創(chuàng)造出的GDP是很多了,但都是無價(jià)值甚至是負(fù)價(jià)值的GDP。
作于2018-3-10