這篇部落格,我把他從《架構之路》移到了《專案管理》。之前把它放到《架構之路》,因為它其實是受到了測試驅動的啟發或影響;但文章完成之後,不光我自己,就連讀者,都覺得這顯然應屬於專案管理的範疇。
需求文件可測試化
我第一點想到的,就是需求文件應該可測試化。我不太明白,這樣簡單有效的一個工作,為什麼幾乎沒有人去做?因為發包方的原因?
大家接觸到的需求文件是什麼樣子的?我從來沒有看到過一份我滿意的需求文件。都是word格式的,文字加上圖片說明。文字都是簡單的陳述句說明,比如:使用者名稱不能重複。簡明扼要,是不是?是你媽個逼!sorry,我爆粗口了,但這種文件真的會讓你瘋掉的!
你覺得這是一個很簡單的需求,是吧?然後你就噼裡啪啦的開始編碼了,20分鐘搞定,躊躇滿志,小case啊!
然後噩夢就開始了。
第一次驗收。發包方的眉毛皺了起來,“嗯,其實我想的是那種:當使用者把使用者名稱輸入完成之後,能立即顯示該使用者名稱是否重複,而不用點選提交之後才顯示這個結果,你看XXXX網站就是這樣的”。OK,Ajax效果是吧?好呢,我改。感謝asp.net mvc,有現成的Remote實現,20分鐘,收工,over!
第二次驗收。發包方有點不耐煩了,“你這個還是不對啊!你看我給你說了XXXX網站,如果這個使用者名稱不重複的話,要打個勾啊;錯了,打個叉,再顯示‘使用者名稱重複’的提示啊……”。你有沒有想哭的感覺?我忍,含著淚,找個勾勾叉叉的圖片,再改。
第三次驗收。發包方終於笑了,“不錯不錯!”你心裡一塊石頭落了地。但這發包方思維很嚴密,他突然想到一個問題,“你說,假如兩個使用者同時註冊,用的是同樣的使用者名稱,這個使用者名稱也是以前資料庫裡沒有的。所以頁面輸入的時候,肯定是顯示使用者名稱有效,是吧?所以他們都可以提交,但他們使用者名稱又是一樣的,都提交了就重複了,這時候會發生什麼情況?……”你仔細的想了想,是不是覺得晴天霹靂天旋地轉?
這只是一個非常非常簡單普通的需求,都可以演化出無數種具體實現,更何況其他你可能從來沒接觸過的複雜需求?你作為一個程式設計師,只是覺得苦逼鬱悶或者還得趕工加班;但對於公司來說,這就是個大麻煩了:工期延誤、費用增加、信譽破產……這時候該追究誰的責任?我們可以設想這樣的對話:
發包方向你們公司老大抱怨:“張總,你們這個專案都延期好幾次了,我不好交代了呀!”
公司老大張總叫來專案經理:“這個專案怎麼回事?趕快去催一下。”
專案經理找到你:“怎麼一個功能做這麼久?你是怎麼搞的?”
你:“我怎麼搞的?需求改了N遍了!我還想問你是怎麼定的需求呢?你看……”
專案經理找到公司老大:“張總,這個客戶的需求變了啊!一直改需求,我們……”
公司老大找到發包方:“這個劉總,這個專案你們改了需求啊!不光這個工期不行,費用也得考慮考慮啊,呵呵!”
發包方就炸了,“還要加錢?你們這些個奸商!合同不是寫得好好的,一口價……”
“不是改了需求麼?”
“我哪裡改?就不過‘使用者名稱不能重複’啊,我改成了‘使用者名稱可以重複’?”
“不是。我們最開始以為……,結果……”
“你們為什麼最開始不提出來呢?你們是專業人士,你們應該把這些問題考慮到的呀!還虧得我們思慮周全,我們業餘人員都能想到的,你們怎麼就想不到呢?你們怎麼樣的一個業務素質?……”
(程式設計師同學,說句題外話,老闆在外面受的氣不是你們所能想象的。你以為他們在外面吃喝玩樂好不瀟灑,他們實際上就一個“三陪”而已,陪吃陪喝陪笑,不比做小姐的好到哪裡去!都是給你們“背黑鍋”“揩屁股”啊。)
就事論事,這件事的責任還真賴不到發包方。他的需求最多算是“不明確”,你不能說他“變更”了需求。但是,不明確的需求你接受了,責任就轉移到你的身上了。退一萬步講,你是“專業人士”,你真的應該考慮得比發包方周全。
所以,你觀察周圍經驗老到的程式設計師,他們拿到這個需求是不會立馬動手敲程式碼的。他們一定會想一想,多問幾個具體細節,覺得沒有問題之後,才開始動手。所以他返工就少,他就是可以不加班,這就是人家的本事。
但是,正如我反覆強調的,架構/管理的目的,應該是通過一種制度一種流程來避免上述問題,而不是靠個人技巧經驗。追根溯源,解決這個問題,就應該“明確需求”——也就是說,需求不能這樣懵懵懂懂大而化之,必須相當的明確清晰,具有唯一性。怎麼實現?需求文件可測試化。或者你可以理解為:把需求文件像測試文件一樣寫。比如,上述需求就變成:
- 在註冊頁面
- 輸入使用者名稱xxxx,移出焦點,頁面不重新整理,顯示圖示“叉”和錯誤提示:使用者名稱重複
- 輸入使用者名稱yyyy,移出焦點,頁面不重新整理,顯示圖示“勾”
- ……
這份需求/測試文件,交發包方稽核確認;之後驗收,就嚴格按該文件逐項進行,所有測試通過,驗收合格。如果需要加需求,一般都得加測試;加測試,那不用說,我們就來談談時間費用的問題。
BuildDatabase
在一些文件規範嚴格的公司,實際上是同時有一份開發文件和測試文件的。但開發人員在整個開發過程中,並沒有參考測試文件,所以最後就很容易造成測試階段開發人員不斷地返工,開發人員和測試人員之間矛盾尖銳。但最終80%的情況,還是開發的問題,畢竟你是做事的人,人家是檢查的人。所以為什麼不從一開始,就把需求文件、測試文件和開發文件合其為一呢?(當然,分開寫,會有一些“監督”的作用,但我始終覺得,這種監督效率不高,投入產出不划算)
而且我發現,這些測試文件,都有一個問題:操作繁瑣不經濟。
仍以“使用者名稱不能重複”為例,我看到的測試文件大致就是這樣寫的:
- 以test-1112為使用者名稱註冊一個新使用者
- 再使用test-1112為使用者名稱進行註冊
- 頁面顯示錯誤提示:使用者名稱重複
- ……
表面上看起來沒有問題,但是如果測試次數多了的話,就會感覺每次先去註冊一個新使用者很麻煩。而且,這只是最簡單的功能,如果功能複雜,要求準備的資料多/難/特殊,怎麼辦?我們又講一個故事吧。
專案上線前夕,測試人手不夠,我們開發過去幫忙。我跑一個test case,跑了一個多星期!你信不信?我的問題就在於測試的這個資料做不出來。測試文件是一步步寫清楚了的,但你這樣做不下去:許可權不夠、資料已有變化、文件模糊……到處找人問。找到懂這事的人,中途又發現,程式(頁面)發生了更改,有些功能跑不起來……最後一個workaround,得改資料庫,資料庫又得找DBA啊……總之,這個test case搞得雞飛狗跳,好在最後還是跑出來了——但以後(下一次)怎麼辦, 我就不知道了。
本質上,這種做法,測試資料是要測試人員自己“做”,或者“找”的,有很多問題。所以,從那時開始,我就在想:能不能把測試用的資料“固化”下來?讓測試人員就基本上不用“做”,或者很方便的就能“找”打測試資料。比如:“使用者名稱不能重複”的文件就直接寫成:
- 使用test-1112為使用者名稱進行註冊
- 頁面顯示錯誤提示:使用者名稱重複
最多加一個說明:test-1112是已有使用者名稱,使用者名稱不能重複。
這個誘惑一直吸引著我,最後我在solution中就引入了一個BuildDatatase專案,專門為開發測試準備資料。毫無疑問,這個決定也遭到了開發人員的抵制。因為這個資料也不是那麼好做的,具體我們將在專案詳解裡談。
我鐵腕推行,大概經過了兩件事,他們慢慢的就習慣/認同了這種做法。
第一件事,是任務列表頁面。開發程式碼之前是寫好了的,而且已經跑了一段時間了。我讓他為該頁面重新建立測試資料,他一臉不可置信:這種篩選排序,天?要準備多少資料?!怎麼準備?程式碼都寫好了,跑得好好的,有必要嗎?
我讓他先冷靜一下。然後有以下對話:
“如果你不準備這些資料,你怎麼保證你程式碼的正確性?”我問。
“就直接在頁面上建幾個任務啊,然後跑一下。”他想了想。
“要建幾個呢?”我繼續追問。
“……”,他一時答不出來。
“隨便做幾個資料,隨便的跑一跑,然後就不管了,是吧?你以前就是這樣做的?”,我接著說,“所以我們的程式碼沒有質量。然後如果沒有專門的測試呢,就讓使用者當免費測試員;有測試呢,我們就把這些髒活累活扔給他們。他們一遍遍的報bug跑不過,我們還不服氣還有怨氣……為什麼我們不一開始就把它做好呢?”
首先確定要做,然後再想辦法!很快我們就想到了一個辦法,反過來做:滿足所有篩選條件的任務就一條,然後逐個的減少篩選條件,每少一個篩選條件,就增加一個適格任務。這樣理下來,共計25條資料就夠了,並沒有我們想象的多。從構思到文件再到最後“造”完所有任務,差不多花了一個下午的時間而已。最後,戲劇性的是:出效果了!跑一遍,我們立馬發現了程式碼裡面的兩個bug。因為這種query查詢寫程式碼的時候都“複製貼上”的,十幾個條件,難免出錯。如果不是這樣跑一次,這些bug不知道什麼時候能冒出來——因為我們自己用的篩選條件比較固定的,某些篩選條件從來沒用過。所以我問他:你之前說“跑得好好的”?他只能呵呵了。
第二件事,是在任務編輯頁面,我們要測試父子任務關係之間的自動化功能,這就需要比較複雜的一個“任務樹”做開發測試資料,好在我們是做了的。程式碼review的時候,我在我這邊一跑,不對,就直接打回去了;過了一會,他跑過來,程式碼沒問題啊?當時我們腦子都短路,沒想到其他,就先去看程式碼去了,折騰了好久。在他電腦上跑,然後又在我電腦上跑,又是設斷點,又是看邏輯。
後來我突然靈光一閃,“是不是資料的問題?”
“嗯,有可能。但怎麼確定呢?”
“你重新BuildDatabase再跑!”
果不其然,原來他自己跑的時候改動了資料,然後就忘了呀,一直就在已變動的資料上跑,當然沒問題了。虧得我們有BuildDatabase,可以隨時重建“基準”資料,否則,這種問題是相當花冤枉時間的!
這類似的情況其實很多很多,測試人員報bug之後,我估計開發人員最常用的一句臺詞就是:“我這裡都能跑啊?!”所以問題80%都出在資料上——那為什麼我們的資料就不能規範統一起來呢?
通過BuildDatabase,建立一個有序的可控的資料庫,我們才能夠在此基礎上進行一系列的(測試驅動的)文件編寫、開發和測試活動。所以說,BuildDatabase是“測試驅動”的基石和保障。除此以外,BuildDatabase還可以在專案釋出、模擬展示等方面發揮重要作用。
(我以前公司的整合測試環境這些,資料庫都是從生產環境中copy或擷取的,我們需要的資料都是“自己造”,或者“自己找”的。這樣做能基本滿足開發測試需要,但中間總是很容易“出簍子”——正如我前面所說,一個test case可以跑一週。而且隨著資料增加,這個copy也越來越難啊,一次導幾百個G終究很累,所以好像是到了一定時候,還是得自行維護測試資料庫——但維護主要是資料結構上的,比如增減列之類的,資料本身是無法維護的。
我很好奇,除了我的builddatabase以外,有沒有其他比較可行的辦法?歡迎大家踴躍交流!)
老規矩,說專案進展情況。任務管理系統修了一些小bug,主要是“任務樹(關係)”可以向上向下自由展開了。接下來的主要工作是完善文件,方便新加入的同學能迅速上手跑原始碼。唉~~~想到寫文件就苦逼啊!我最愛程式碼,恨文件!!!