單元測試就是常說的Unit Test
而TDD其實(shí)是uTDD - unit Test Driven Development

在我現(xiàn)在的日常工作中,其中一個很重要職責(zé)就是保證新人快速成長。這里的“成長”不是應(yīng)試教育下的快速成長,更不是通過填鴨式教育、題海戰(zhàn)術(shù)來達(dá)到的成長。而是以理解公司文化的為前提,“素質(zhì)教育”為基礎(chǔ)的健康成長。
我們公司的創(chuàng)始人是《敏捷宣言》提出者之一,哪怕在這樣一家敏捷絕對擁躉的科技公司里,也一樣會有關(guān)于TDD的一些常見問題。我在和新人Pair、帶團(tuán)隊(duì)和
客戶參觀的過程中都無一幸免的被問到了如下TDD的問題:
- 為什么我們要TDD ?
- 任何時(shí)候都要TDD嗎?什么情況下不用TDD?
- TDD怎么衡量她的價(jià)值?實(shí)踐她的都是開發(fā)人員,她到底有沒有價(jià)值因該開發(fā)人員說了算。
這些問題的提出者以及提出時(shí)間,總結(jié)起來看還挺有意思。有的完全沒有接觸實(shí)踐過敏捷;有的相信敏捷,但還沒有付之實(shí)踐;還有的擁抱后堅(jiān)持實(shí)踐了一段時(shí)間,思考然后,最終提出反問。
有這些問題其實(shí)很正常,回答起來也不難。我們大可以把TDD的好處羅列一翻,把一些正確的大道理再敘述一遍,但我們前面可是標(biāo)榜過我們自己是“素質(zhì)教育”,我們更希望對一些基礎(chǔ)知識認(rèn)真打磨,讓大家根據(jù)這些基礎(chǔ)知識結(jié)合自身的環(huán)境,給出自己的判斷。學(xué)以致用,融匯貫通。
我們這里說的TDD(測試驅(qū)動開發(fā)),實(shí)際上更準(zhǔn)確的描述為單元測試驅(qū)動開發(fā)。先不談怎么驅(qū)動開發(fā),我們一起來掀開單元測試的外衣,看看到底本尊長什么樣?怎么就可以驅(qū)動開發(fā)了?他能解決什么問題?又對什么問題有心無力?
Unit testing is when you write test code to verify units of code.
翻譯起來就是:單元測試就是用來驗(yàn)證代碼單元正確性所寫的測試代碼
我們下面站在 先實(shí)現(xiàn)后測試 的原生視角來回答幾個問題
“單元”到底怎么定義?
- 在尺寸上并沒有清晰的定義。
- 一小段展現(xiàn)出某些“有用的行為”的代碼
- 一個單元自身 通常 不表示完整的端對端(end-to-end)行為,它只表示這個端對端行為中的一個子部分。
可以看出這里的“單元”,并沒有固定和清晰的定義,比較容易接受的定義是把“單元”和函數(shù)劃上等號。項(xiàng)目管理中所有的實(shí)踐,最終都是為了保證項(xiàng)目的按時(shí)交付及
質(zhì)量保證。而且明確說明單元測試不因該表示完整的端對端行為,因?yàn)樵陧?xiàng)目的測試體系里,比如金字塔原理,越接近用戶的測試往往是最費(fèi)時(shí)的,所以從成本和質(zhì)量平衡的角度來看,因該是越精簡越好,這其實(shí)就是對單元測試提出了明確的要求-集成測試和端到端測試沒有覆蓋到的,那么單元測試你就給我保證好,至于你用函數(shù)分還是以行為分,沒有統(tǒng)一的標(biāo)準(zhǔn)。這里借助絕對正確的一句廢話-對項(xiàng)目合適的才是最好的。
何時(shí)、何故需要寫單元測試?
- 你 剛剛 完成了一個特性(Feature)的編碼并且想確保它能夠按照預(yù)期工作。
- 你想用文檔記錄一個代碼中的修改,以便你或其他人能夠在以后明白相關(guān)的意圖。
- 你需要修改代碼,同時(shí)希望確保這些即將來臨的修改不會破壞任何已經(jīng)存在的行為。
- 你希望了解當(dāng)前系統(tǒng)中的行為。
- 你希望知道第三方代碼的行為在什么時(shí)候會與你的期望不符。
簡單來說也就是對測試特性的另一種描述,通常我們可以把測試當(dāng)成文檔,因?yàn)閱卧獪y試要足夠的簡單,不然就會有人問這樣的問題:我們用什么來保證測試本身的正確性呢?是不是得為我們的測試再寫測試呢,顯然不是,不然不就死循環(huán)了嘛。但這不意味著我們就不用保證測試本身的正確性了,簡潔性其實(shí)就是這個目的,你一眼就能看出來正確與否,那么我們大家都有信心相信我們測試代碼的質(zhì)量,以及功能模塊的穩(wěn)定性也可以通過測試得到了因有的保障。
推薦的測試結(jié)構(gòu)
- AAA (Arrange - Act - Assert)
- GWT (Given - When - Then)
需要注意的是,通常我們建議從結(jié)果開始(Then/Assert),明確我們希望得到的結(jié)果,再設(shè)置好測試環(huán)境并觸發(fā)相應(yīng)的動作。
推薦的測試原則 - FIRST
- Fast : 好的測試因該足夠快 - 因?yàn)閱卧獪y試的價(jià)值在于持續(xù)、全面、快速的反饋系統(tǒng)的健康程度
- Isolated: 好的測試因該相互隔離 - 你應(yīng)當(dāng)能夠在任何時(shí)間,以任何順序,運(yùn)行任何一個測試。
- Repeatable:好的測試應(yīng)該可復(fù)驗(yàn) - 每一個測試應(yīng)當(dāng)在每一次運(yùn)行時(shí)產(chǎn)生與之前一樣的結(jié)果。
- Self-Validating - 好的測試應(yīng)該能夠自認(rèn)證!- 寫測試是為了節(jié)省時(shí)間而不是為了花費(fèi)更多的時(shí)間。所以,測試應(yīng)當(dāng)能夠自排列、及時(shí)、恰到好處的自動化運(yùn)行,并且能夠快速、準(zhǔn)確的確認(rèn)結(jié)果,盡可能的不需要手動操作或設(shè)置。
- Timely - 好的測試應(yīng)該足夠及時(shí)!- 單元測試是一個好習(xí)慣,你越是推遲利用單元測試來驗(yàn)證你的代碼,就越是需要付出更多的代價(jià)。同理,一旦你將代碼提交進(jìn)了代碼庫,你專門找時(shí)間回過頭來補(bǔ)測試的機(jī)會也就很小了。
推薦的測試邊界原則 - CORRECT
- Conformance - 一致性 (值是否符合預(yù)期的格式?)
- Ordering - 有序性 (一組值的順序是否符合預(yù)期?)
- Range - 區(qū)間性 (值是否在一個合理的最大值和最小值的范圍內(nèi)?)
- Reference - 引用性 / 耦合性 (代碼是否引用了一些不受其直接控制的外部因素,由這些外部因素所引入的前置條件或后置條件所造成的影響是否符合預(yù)期?)
- Existence - 存在性 (值是否存在(例如:非 null,非零,存在于某個集合中等)? )
- Cardinality - 基數(shù)性 : 計(jì)數(shù)性 (是否恰好有足夠的值?(重點(diǎn)關(guān)注 0-1-n 原則,問題往往發(fā)生在這三個邊界上。))
- Time - 時(shí)間性(絕對時(shí)間及相對時(shí)間)- (所有事情是否按順序發(fā)生?是否在正確的時(shí)間?是否及時(shí)?)
結(jié)束語
從單元測試的基本概念可以看出來,更多的情況下,并沒有嚴(yán)格意義上的定義,更應(yīng)該堅(jiān)持原則,看清形勢,找到最適合項(xiàng)目團(tuán)隊(duì)的。
TDD也可以簡單的看作基于單元測試,所采用的敏捷實(shí)踐之一,持續(xù)改進(jìn)也是他們不謀而合的地方,也是我推薦TDD的原因之一。
下一篇會以TDD實(shí)例從代碼實(shí)踐來講解因該怎么做TDD。
歡迎更多的討論,更浩渺的思維!

可通過以下方式聯(lián)系我們(You can contact us the ways below):