讓我們詳細地說明
作為開發(fā)人員,我們都知道我們應該測試我們的代碼。我們應該寫單元測試,但這也通常是我們發(fā)現(xiàn)沒時間時跳過的第一步。
作為團隊的領導者或者管理者我們都知道測試是必要的,但是當截止日期臨近的時候,我們傾向于減少測試,而把更多的重點放到編碼上。
這樣看測試領域似乎很緊張。我們都知道測試對我們是有利的,但是一旦項目面臨壓力時我們就不再測試了。
我們?yōu)槭裁礈y試?
Edsger W Dijkstra 說過:測試可以用來找到顯式的缺陷(bug),但是無法顯示潛伏的軟件缺陷(bug)。
這意味著測試不能百分百保證你的軟件沒有缺陷(bug),但是它確實很有幫助。我們也可以換種說法,如果我們不進行測試我們幾乎可以百分百保證我們 的軟件會有缺陷(bug),除非我們是在編寫像“hello world!”那樣簡單的程序。但是即使這么簡單的程序你也會測試,因為一旦你輸入完你的代碼你就會很好奇它的輸出是不是真的是“hello world!”。
而這就是第一類形式的測試,也是我們一直在做的: 手工測試. 我們編寫程序,然后啟動它去檢驗運行結(jié)果。 對于一個簡單的“hello world”這可能是足夠的,但是對于復雜度更高的程序這可能會導致時間的浪費,這是對一個已知的行為結(jié)果集的手工重復。這難道不是我們發(fā)明計算機的初衷 嗎?
對于“hello world”這不是大問題,但是當你創(chuàng)建一個web應用時,測試場景是在翻頁十次,點擊某些按鈕,在大量表單中輸入(正確的)數(shù)據(jù)之后再測試某些特定條 件,你就看到自動化會節(jié)省大量的時間。如果你能通過測試運行器(test runner)直接執(zhí)行你想要測試的函數(shù),而不是必須花費半分鐘手工執(zhí)行到那個函數(shù),你會節(jié)省很多時間!
但這也意味著我們需要多一點點編程,而更多的編程意味著更多的時間和精力。所以它會花費更多的時間而你的項目可能因此完工的晚些。
也許未必
讓我們創(chuàng)建一個控制臺應用程序來計算最大公約數(shù)(GCD)的兩個整數(shù)。有很多方法可以解決這個問題,但為簡單起見,我們將
1.輸入兩個整數(shù)
2.不管其算法怎么樣,計算一下 GCD
3.顯示輸出
讓我們?yōu)g覽一下正常的開發(fā)周期。我們通常寫一個 main() 函數(shù),得到了兩個整數(shù),以及調(diào)用一個函數(shù)來計算一下 GCD,然后顯示結(jié)果。
測試。在你的控制臺中輸入 2 個整數(shù)會花一些時間,這將變得相當無聊,如果你需要多次重復你的代碼。這也很容易在控制臺應用程序中輸入出錯,導致程序崩潰。這意味著你必須重新啟動程 序,輸入兩位數(shù),然后再次驗證結(jié)果。請你要記住,我們討論的是一個控制臺應用程序,只需要兩個輸入值,不需要點擊(在 web 應用程序中),我們已經(jīng)看到,這將需要花費一些時間。
然后,我們很可能會想要測試一些更多意味著重啟程序的值,進入兩位數(shù)(正確地),然后測試。。。所以我們即使看到也不會立即這樣做,因為它要花費太多的時間。Edge 案例將會被遺忘,錯誤只會在生產(chǎn)中被發(fā)現(xiàn)!
此外,當我們改變一些我們需要再次運行所有的測試(手動),使用一個被遺忘的,或者使用快捷鍵的高風險的測試。
在那兒,不會有跟蹤我們的測試工作。不寫入日志文件,在整個測試期間,除非你增加這個你做的事情列表工作(手動)。
消極反饋循環(huán)
通常,當項目(因為某種原因)延期了,則容易陷入一種消極反饋循環(huán)。有時我們會先決定跳過編寫測試代碼,而這則會造成情況如下圖所示:
項目延期,造成我們不得不去編寫更多的代碼。所以與其“浪費時間”去不停地測試代碼,不如不停地去開發(fā)項目。而這樣做的結(jié)果就是代碼質(zhì)量進一步下 降,并最終(或早或晚)導致返工。返工又通常會在最有限的時間里變得十分緊急(有些人叫這種現(xiàn)象為“墨菲是個樂天派!”)。其實返工什么也改變不了,項目 現(xiàn)在只會進一步被延遲。很奇怪吧,我們編寫越多的代碼,我們的項目完工越晚。一種常用應對措施是讓更多的開發(fā)人員被參與到項目的研發(fā)中,然而這樣的作用也 只是加劇消極反饋循環(huán)而已。
若項目缺乏測試,在驗收和生產(chǎn)環(huán)境時,通常用戶則會發(fā)現(xiàn)許多 bug,這將會快速地降低用戶對項目的信任度,從而產(chǎn)生消極反饋。這種反饋傳遞給(工作過度的)開發(fā)人員,就造成開發(fā)人員“疲勞”現(xiàn)象。后果就是開發(fā)人員 工作積極性下降,開發(fā)人員離職,……,項目又進一步延遲了。
打破消極循環(huán)
我想你已經(jīng)想到有一個辦法可以解決這種現(xiàn)象。讓我們來繪制一幅不同的場景:
我們可以從一個理想計劃“項目按時完工”開始。我們開發(fā)代碼,然后立即測試它。測試最好是自動化(編碼實現(xiàn))的,這樣我們可以輕松有效的去執(zhí)行它 們。我們把開發(fā)和測試緊密的結(jié)合在一起,每個開發(fā)測試循環(huán)可以很快速的執(zhí)行。當一個開發(fā)測試循環(huán)結(jié)束時我們有信心保證代碼質(zhì)量是很高的,因為它已經(jīng)通過了 測試。而且用戶因為發(fā)現(xiàn)缺陷(bug)的數(shù)目變少而對我們繼續(xù)高度信任。即使他們發(fā)現(xiàn)了一個缺陷(這依然是有可能的),我們也可以擴充我們的測試集合,去 避免相關缺陷的重現(xiàn)。
如此下去,返工將不再是必須的,項目得有繼續(xù)。
如果我們的項目已經(jīng)延期了,就需要我們花些時間來采用這種方法論。對新功能的凍結(jié)也許是必須的。停止開發(fā)新的代碼,取而代之去為最嚴重的(惱人的-清晰的-高代價的)缺陷編寫測試。
項目延期的情況下再去為你完整的代碼庫編寫測試是不可行的,只針對其中的一些部分就可以,不要去浪費你的時間。但是要記住其它部分也還是需要編寫測 試的。我在這種情況下會去找出最嚴重的問題(劃分優(yōu)先級),然后為它們編寫測試。之后“快速”修改代碼就會變的更容易,并且可以保證在修改其他部分是它不 會出錯。自動化測試可以很頻繁的執(zhí)行,從而降低了缺陷(bug)重現(xiàn)的風險。好了,現(xiàn)在可以開始去有效的強健我們項目了
上面這些通常會要求進行代碼重構(gòu),從而使它可測試化。我會在另一篇文章里介紹它。