開心一笑
【朋友病了,要掛鹽水。給他扎針的是一個實習小護士,扎了半天都沒扎進血管。
他痛得齜牙咧嘴,無奈叫來了護士長。
護士長好手法,只見她一針見血地扎進了血管,然后馬上拔出來,
把針遞給那個實習護士說:“看清楚沒有?你再試一次!”】
提出問題
如何優(yōu)雅編寫測試代碼???
解決問題
開發(fā)工作當中,我們經(jīng)常需要編寫很多接口,但是往往忽略了單元測試。即使使用了單元測試,對于大部分人來說,也是用過既仍。編寫一些簡潔,可復用的單元測試時非常有必要的,以下是來自《代碼整潔之道》的幾點總結(jié):
1)TDD(測試驅(qū)動開發(fā)),意思是先寫單元測試,然后寫對應的代碼,通過修改調(diào)試讓寫的代碼通過單元測試。使用TDD,會使測試覆蓋所有的代碼,測試代碼和生產(chǎn)代碼的比例有可能會達到1:1 ,所以也會帶來成本的問題,所有我們要保持測試的整潔。
2)單元測試的好處:讓代碼可擴展,可維護,可復用。
3)整潔測試的三要素 :可讀性、可讀性、可讀性。
4)每個測試都可以拆分為三個環(huán)節(jié):構(gòu)造測試數(shù)據(jù)、操作測試數(shù)據(jù)、檢驗操作是否達到預期結(jié)果。
5)雙重標準:測試環(huán)境中和生產(chǎn)環(huán)境中有些條件不必完全一致。生產(chǎn)環(huán)境中有時要考慮內(nèi)存、CPU等性能問題,而在測試環(huán)境中不必做這些限制。
6)一個測試一個斷言,不必完全糾結(jié),但單個測試斷言數(shù)應該最小化,只測試一個概念,還是單一職責的問題;
7)F.I.R.S.T原則
F Fast:測試需要頻繁運行,因此要能快速運行;
I Independent:測試應該相互獨立;
R Repeatable:測試應當能在任何環(huán)境中重復;
S Self-validating:自足驗證,測試應該能看到成功與否的結(jié)果;
T Timely:測試應該及時編寫,應該恰好在生產(chǎn)代碼之前編寫;
下面用一個例子說明上面的理論(理論很枯燥,實例很美好):
package com.hwy.test;
import org.junit.Assert;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
public class CodeCleanTest {
@Test
public void testBuySnacks(){
List<String> snacks = buySnacks();
Assert.assertEquals("購買零食有問題,請檢查!!!",true,snacks.size()>0);
}
@Test
public void testEatSnacks(){
/** 這個是我們自己構(gòu)建的零食數(shù)據(jù),肯定不會出現(xiàn)null等問題 **/
/** 我們不要調(diào)用buySnacks方法,因為還是單一職責的問題,只測試一個概念**/
List<String> snacks = getSnacks();
Assert.assertEquals("沒有一起吃3個零食", 3, eatSnacks(snacks));
}
@Test
public void testDropLitter(){
List<String> snacks = getSnacks();
Assert.assertEquals("垃圾沒有扔掉!!!",true,dropLitter(snacks));
}
@Test
public void testDateWithGirl() throws Exception{
Assert.assertEquals("約會失敗了", true, dateWithGirl());
}
/**
* 構(gòu)建零食數(shù)據(jù)(事實這些注釋都是不需要,只是為了大家理解)
* @return
*/
public List<String> getSnacks(){
List<String> snacksList = new ArrayList<>();
snacksList.add("牛奶");
snacksList.add("巧克力");
snacksList.add("土豆片");
return snacksList;
}
/**
* 買零食(事實這些注釋都是不需要,只是為了大家理解)
* @return
*/
public List<String> buySnacks(){
List<String> snacksList = new ArrayList<>();
snacksList.add("牛奶");
snacksList.add("巧克力");
snacksList.add("土豆片");
/** 這里故意顯示為空 **/
//return null;
return snacksList;
}
/**
* 約會
*/
public boolean dateWithGirl() throws Exception {
boolean isSuccess = false;
List<String> snacksList = buySnacks();
/** 利用逆向思維,拋出一個業(yè)務異常,這里我只是用簡單的exception代替 **/
if(null == snacksList || snacksList.size() ==0){
throw new Exception("你沒買到零食或買到的零食有問題,請檢查!");
}
/** 代碼執(zhí)行到這一步就說明snacksList不為null,之后的所有
* 操作都不用判斷snacksList是否為空 **/
List<String> litter = eatSnacks(snacksList);
dropLitter(litter);
isSuccess = true;
return isSuccess;
}
/**
* 吃零食(事實這些注釋都是不需要,只是為了大家理解)
* @param snacks
*/
public List<String> eatSnacks(List<String> snacks){
for (String snack : snacks) {
System.out.println("一起吃" + snack);
}
return snacks;
}
/**
* 仍垃圾(事實這些注釋都是不需要,只是為了大家理解)
* @param litter
*/
public boolean dropLitter(List<String> litter){
boolean isDrop = false;
for(String snack:litter){
System.out.println("扔掉垃圾:" + snack);
}
/** 這里故意寫的有點冗余,只是方便大家學習 **/
isDrop = true;
return isDrop;
}
}
上面的每個測試方法都是:test + 原方法名稱,一般測試方法都是這么命名的。同時上面的測試方法,每個方法都是獨立的,只測試一個概念。
下面舉一個錯誤的實例:
package com.hwy.test;
import org.junit.Assert;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
public class CodeCleanTest {
@Test
public void testBuySnacks() throws Exception{
/** 像這樣子,在一個測試方法中,測試太多的方法,造成測試用例難以復用 **/
/** 這樣不符合每個測試一個斷言的規(guī)則,同時也不符合F.I.R.S.T原則**/
List<String> snacks = buySnacks();
Assert.assertEquals("購買零食有問題,請檢查!!!",true,snacks.size()>0);
Assert.assertEquals("沒有一起吃3個零食", 3, eatSnacks(snacks));
Assert.assertEquals("垃圾沒有扔掉!!!",true,dropLitter(snacks));
Assert.assertEquals("約會失敗了", true, dateWithGirl());
}
/**
* 構(gòu)建零食數(shù)據(jù)(事實這些注釋都是不需要,只是為了大家理解)
* @return
*/
public List<String> getSnacks(){
List<String> snacksList = new ArrayList<>();
snacksList.add("牛奶");
snacksList.add("巧克力");
snacksList.add("土豆片");
return snacksList;
}
/**
* 買零食(事實這些注釋都是不需要,只是為了大家理解)
* @return
*/
public List<String> buySnacks(){
List<String> snacksList = new ArrayList<>();
snacksList.add("牛奶");
snacksList.add("巧克力");
snacksList.add("土豆片");
/** 這里故意顯示為空 **/
//return null;
return snacksList;
}
/**
* 約會
*/
public boolean dateWithGirl() throws Exception {
boolean isSuccess = false;
List<String> snacksList = buySnacks();
/** 利用逆向思維,拋出一個業(yè)務異常,這里我只是用簡單的exception代替 **/
if(null == snacksList || snacksList.size() ==0){
throw new Exception("你沒買到零食或買到的零食有問題,請檢查!");
}
/** 代碼執(zhí)行到這一步就說明snacksList不為null,之后的所有
* 操作都不用判斷snacksList是否為空 **/
List<String> litter = eatSnacks(snacksList);
dropLitter(litter);
isSuccess = true;
return isSuccess;
}
/**
* 吃零食(事實這些注釋都是不需要,只是為了大家理解)
* @param snacks
*/
public List<String> eatSnacks(List<String> snacks){
for (String snack : snacks) {
System.out.println("一起吃" + snack);
}
return snacks;
}
/**
* 仍垃圾(事實這些注釋都是不需要,只是為了大家理解)
* @param litter
*/
public boolean dropLitter(List<String> litter){
boolean isDrop = false;
for(String snack:litter){
System.out.println("扔掉垃圾:" + snack);
}
/** 這里故意寫的有點冗余,只是方便大家學習 **/
isDrop = true;
return isDrop;
}
}
讀書感悟
來自《窮查理智慧書》
- 忙碌的人們很少有閑客來訪;沸騰的鍋里絕不會落入蒼蠅。
- 失意時,沒有人了解;得意時,不了解自己。
- 憤怒總是有一定的理,但很少有好的道理。
- 你可能有時犯大錯,是因為你認為自己永遠正確。
- 一百個小偷,也偷不走一個人的臉皮,特別是當這個人沒有臉皮的話。
- 人們經(jīng)常把自己看錯了,卻很少把自己忘記了。
- 無人認錯的爭吵必然延續(xù)無盡。
- 如果你的秘密不想被敵人知道,請先對你的朋友保密。
- 教訓換來的一兩智慧,勝過書本一斤的知識。
其他
如果有帶給你一絲絲小快樂,就讓快樂繼續(xù)傳遞下去,歡迎轉(zhuǎn)載,點贊,頂,歡迎留下寶貴的意見,多謝支持!