最近有朋友提出意見,覺得我寫的文章比較空洞,寫的很長,但是很不實在?赡茉蚴沁@樣的:代碼太少了。今天就從一段代碼開始吧,這段代碼描述電信營業系統中的繳費開機的過程:



















這是電信營業系統中最常見的的一個業務。電信系統最基礎的模型應該說是"三戶模型",三戶模型描述的是客戶(Customer)、帳戶(Account)、用戶(User)以及服務(Service)等等概念的一個關系模型。實際的模型比代碼里的復雜,這里簡化了很多,主要用來舉個例子。
要開發一個電信系統,首先要做的就是在系統中實現最基礎的幾個模型,比如三戶模型、聯機指令模型、業務辦理模型、合作伙伴模型……,其中三戶模型處于非常重要的地位。首先要做的是在軟件系統中真實的反映這個模型,絕不可以將其隱含擴散在各個業務過程中。然后就可以在這個基礎上,從一到二、從二到三、從三到萬,實現各個子系統和復雜多變的業務需求。
按照TDD的開發思路,我們應該先寫測試代碼,再來寫實際程序,用測試來推動開發的前進。這里先把代碼寫出來,表達一下業務。下面我就說一下,單元測試代碼應該測什么。
我們拿User舉個例子,現在我們要寫User類的測試代碼。
需要測什么?
我們需要測試User類的外在表現。比如我們要測試pay方法,應該這樣:
我們建立一個User對象,這個User余額是0,欠費38元,F在繳費100元,繳費完成后,余額應該是62元,欠費應該為0,這是一個Case。
我們建立一個User對象,這個User余額是30,欠費0,F在繳費50元,繳費完成后,欠費應該仍然是0,余額應該是80元。這是第二個Case。
我們建立一個User對象,這個User的余額是0,欠費150元,F在繳費70元,繳費完成后,欠費應該是80元,余額應該是0。這是第三個Case。
我們應該測試的是User類的外在表現,而不應該過問他如何實現。
在測試的時候,我們需要一個可以重復的穩定的環境(真實環境往往不行),有時候無法直接建立User對象(比如User對象要依賴一個數據集),有時候真實的環境很難實現一些測試條件(比如邊界值、非正常值)。這時候,我們就可以使用Mock、Stub這樣的方法,把User建立起來,也把環境建立起來,然后測試User的表現。
不需要測什么?
還是拿User舉例子,還是測試pay方法,我們不應該測試這些東西:
繳費如何實現,這個費用保存到哪個數據表的哪個字段里去了,是否符合某種數據關系。
繳費以后的余額、欠費按照什么變量加減或者從某個數據字段中得到自己的值。這個不需要測。User類應該負責自己的費用問題,不應該把任何業務邏輯暴露給其他類(費用保存到哪里、什么數據結構,User應該負責)。外界只關心User的表現。
繳費后應該不應該向交換機發送開機的指令,這個就更不需要測試了,這是User的調用者應該負責的事情。
測試代碼保證了什么?
測試代碼只保證接口是正確的,至于業務正確不正確不是最關心的事情。比如User的測試代碼,User把費用保存在數據表里、還是寫在文件里,他是不關心的。甚至根本沒有持久保存,他也不管,只要接口正確就通過。當然,內在實現上的錯誤總會通過接口表現出來。假如User沒有把用戶繳費保存起來,當我們在另外的空間中建立相同ID的另一個User對象時,就會得到錯誤的余額和欠費。
單元測試究竟有什么用?
從某種角度上說,單元測試并不能保證業務的正確性,而只能保證接口的正確性(嚴格的說,其實連接口的正確性也無法保證,他只能保證接口"沒有這些錯誤")。如果一個類總是自己在默默的工作著,不提供接口告訴外界他干了什么,測試他的業務是很難的(也不應該去測試,否則必然要插手他的業務)。那么單元測試究竟有什么用呢?
單元測試保證模塊修改后的兼容性?梢酝ㄟ^單元測試來判斷一個模塊在修改后是否與修改前保持兼容、多大程度上不兼容、影響范圍有多大。開發者就可以更加專注于模塊內部的業務。單元測試無法代替人工的工作,他能夠做到的是衡量一個模塊修改后會不會影響其他模塊的工作。這對程序的維護和升級有非常大的好處。
文章來源于領測軟件測試網 http://www.kjueaiud.com/