一直有個疑問,對于遺留系統,我們該如何TDD
?
我個人比較認同TDD是一種設計方法,不能代替真正意義上的測試。是幫助我們設計自己代碼的一種方法。對于遺留系統,面對一堆需求文檔,面對一陀陀已經難
以繼續維護的陳舊代碼,你的心是否哇涼哇涼的
?做為一個使用Rails的開發人員,難道你要把這些代碼翻譯為Ruby代碼嗎
答案當然是不!
很多時候,我們雖然很清楚要TDD,但是很多時候我們是在DDT。基本上是根據遺留系統的代碼把功能代碼寫的差不多了,才想起來寫測試。寫完之後,
用流行的rcov測試一下測試代碼覆寫率,不足的地方再加上測試
? 回頭想想,我們引入TDD的初衷是這樣的嗎
? 大家都心知肚明,并不是這樣的!
重新思考TDD之後,我決定在現做的這個ticket裡完全采用了TDD的方式去開發,今天一天下來,感觸頗深:
1。
昨天拿到php源碼,一看代碼,了不得啊,和我功能相關的源碼檔案之有三個,但代碼卻有三千多行。我該怎麼辦
?我肯定不可能把那三千多行都看完吧。我該如何TDD
T代表Test,測試驅動開發,很顯然,是先有測試,要不談何驅動開發。
但是沒有功能代碼,你測試什麼呀?
既然TDD是一種設計方法,那麼這個測試,代表的應該是你大腦中對這個功能的自我認知。
如果對需求了解不清楚,你是沒有辦法對這個功能産生自我的認知的。硬着頭皮整理了一下需求文檔,在腦中形成一個大概的輪廓之後,就可以動手寫測試了。此功
能是要生成一個報表,就是一個create -> show
的過程。一般人想到的先是頁面,我是一般人,是以我也是從頁面開始的,根據已經明确的需求寫下自己對這個頁面的幻想:
/spec/views/reports/new.html.erb_spec.rb中:
require File.dirname(__FILE__) + '/../../spec_helper'
describe "reports/new.html.erb" do
.... ..
it "should render new.html.erb" do
render "/reports/new.html.erb"
response.should have_tag("form[action=/reports/new]") do
#應該有一個label來表明這個field是company name
with_tag('label',"company name:")
with_tag('select#data_company_id[name=?]',"data[company_id]")
#應該有個下拉清單來讓使用者選擇存在的company name
with_tag('label',"Report Filter by Year:")
#應該有個時間清單來讓使用者選擇生成報表資料的時間段。 with_tag('select#report_filter_by_year[name=?]',"report_filter[by_year]")
end
end
.... ..
end
好了,寫到這,我腦中隻剩下了一點不清楚的需求。也就是說,我如果能把這個測試寫完,腦中對這部分的需求應該就明朗了。繼續看文檔,看php代碼,隻看一
些sql語句。一個疑問就是這個頁面用不用加一個排序的下拉清單
? 這個疑問得問和我們合作的老外Andy,正好他還未到睡覺時間,就問他:
new page whether should need
select down list to sort by some column ? 他回答
: sounds like good .
very good , 我可以把上面這句英語翻譯成rspec代碼:
with_tag('label',"Sort by:")
with_tag('select#sort_by_sorted_on[name=?]',"sort_by[sorted_on]")
這樣我的這個測試就全部寫完了:如下
require File.dirname(__FILE__) + '/../../spec_helper'
with_tag('select#report_filter_by_year[name=?]',"report_filter[by_year]")
with_tag('label',"Sort by:")
with_tag('select#sort_by_sorted_on[name=?]',"sort_by[sorted_on]")
打開autotest,運作,報錯:沒有這個頁面。好辦啊,建個頁面不就得了。建立new.html.erb.
運作測試,報錯,沒有
<label> company name: </label>,
好辦啊,在new.html.erb裡加個label不就得了。
根據錯誤資訊的提示,我很快就把頁面的基本元素寫好了。
但是下拉清單的選項,是需要從資料庫裡查詢來生成options的。那麼就在model裡寫個測試方法了:
#應該傳回一個以年份組成的二維數組
it "should return a array by year" do
year = Entry.get_filter_year
year.should be_an_instance_of(Array)
year[0].should be_an_instance_of(Array)
end
這樣,運作autotest,會報錯,Entry沒有定義get_filter_year方法,好辦啊,加上不就得了嗎。
。。。
。。。
不斷的根據錯誤提示來寫功能代碼。
。。。。
測試終于全部通過。
我的功能代碼也完成了。
我對頁面的那點幻想終于成了現實。
這就是TDD。
這個TDD的過程裡,我沒有開過浏覽器。一點不吹牛,信不信由你!
TDD的好處:
1。幫助你理清思維,去把遺留系統的需求逐漸的挖掘出來,而不用去把客戶給的三千行代碼看完。
2。驅動你的開發。整理需求寫完測試之後,然後隻需要運作autotest來利用failure資訊提示完善你的功能代碼,這個時候開發已經相當輕松了。
3。
這樣寫的功能代碼用rcov測試test覆寫率的時候絕對是百分百覆寫的。