開心一笑
【朋友病了,要掛鹽水。給他扎針的是一個實(shí)習(xí)小護(hù)士,扎了半天都沒扎進(jìn)血管。
他痛得齜牙咧嘴,無奈叫來了護(hù)士長。
護(hù)士長好手法,只見她一針見血地扎進(jìn)了血管,然后馬上拔出來,
把針遞給那個實(shí)習(xí)護(hù)士說:“看清楚沒有?你再試一次!”】
提出問題
如何優(yōu)雅編寫測試代碼???
解決問題
開發(fā)工作當(dāng)中,我們經(jīng)常需要編寫很多接口,但是往往忽略了單元測試。即使使用了單元測試,對于大部分人來說,也是用過既仍。編寫一些簡潔,可復(fù)用的單元測試時非常有必要的,以下是來自《代碼整潔之道》的幾點(diǎn)總結(jié):
1)TDD(測試驅(qū)動開發(fā)),意思是先寫單元測試,然后寫對應(yīng)的代碼,通過修改調(diào)試讓寫的代碼通過單元測試。使用TDD,會使測試覆蓋所有的代碼,測試代碼和生產(chǎn)代碼的比例有可能會達(dá)到1:1 ,所以也會帶來成本的問題,所有我們要保持測試的整潔。
2)單元測試的好處:讓代碼可擴(kuò)展,可維護(hù),可復(fù)用。
3)整潔測試的三要素 :可讀性、可讀性、可讀性。
4)每個測試都可以拆分為三個環(huán)節(jié):構(gòu)造測試數(shù)據(jù)、操作測試數(shù)據(jù)、檢驗(yàn)操作是否達(dá)到預(yù)期結(jié)果。
5)雙重標(biāo)準(zhǔn):測試環(huán)境中和生產(chǎn)環(huán)境中有些條件不必完全一致。生產(chǎn)環(huán)境中有時要考慮內(nèi)存、CPU等性能問題,而在測試環(huán)境中不必做這些限制。
6)一個測試一個斷言,不必完全糾結(jié),但單個測試斷言數(shù)應(yīng)該最小化,只測試一個概念,還是單一職責(zé)的問題;
7)F.I.R.S.T原則
F Fast:測試需要頻繁運(yùn)行,因此要能快速運(yùn)行;
I Independent:測試應(yīng)該相互獨(dú)立;
R Repeatable:測試應(yīng)當(dāng)能在任何環(huán)境中重復(fù);
S Self-validating:自足驗(yàn)證,測試應(yīng)該能看到成功與否的結(jié)果;
T Timely:測試應(yīng)該及時編寫,應(yīng)該恰好在生產(chǎn)代碼之前編寫;
下面用一個例子說明上面的理論(理論很枯燥,實(shí)例很美好):
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方法,因?yàn)檫€是單一職責(zé)的問題,只測試一個概念**/
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ù)(事實(shí)這些注釋都是不需要,只是為了大家理解)
* @return
*/
public List<String> getSnacks(){
List<String> snacksList = new ArrayList<>();
snacksList.add("牛奶");
snacksList.add("巧克力");
snacksList.add("土豆片");
return snacksList;
}
/**
* 買零食(事實(shí)這些注釋都是不需要,只是為了大家理解)
* @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è)務(wù)異常,這里我只是用簡單的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;
}
/**
* 吃零食(事實(shí)這些注釋都是不需要,只是為了大家理解)
* @param snacks
*/
public List<String> eatSnacks(List<String> snacks){
for (String snack : snacks) {
System.out.println("一起吃" + snack);
}
return snacks;
}
/**
* 仍垃圾(事實(shí)這些注釋都是不需要,只是為了大家理解)
* @param litter
*/
public boolean dropLitter(List<String> litter){
boolean isDrop = false;
for(String snack:litter){
System.out.println("扔掉垃圾:" + snack);
}
/** 這里故意寫的有點(diǎn)冗余,只是方便大家學(xué)習(xí) **/
isDrop = true;
return isDrop;
}
}
上面的每個測試方法都是:test + 原方法名稱,一般測試方法都是這么命名的。同時上面的測試方法,每個方法都是獨(dú)立的,只測試一個概念。
下面舉一個錯誤的實(shí)例:
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{
/** 像這樣子,在一個測試方法中,測試太多的方法,造成測試用例難以復(fù)用 **/
/** 這樣不符合每個測試一個斷言的規(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ù)(事實(shí)這些注釋都是不需要,只是為了大家理解)
* @return
*/
public List<String> getSnacks(){
List<String> snacksList = new ArrayList<>();
snacksList.add("牛奶");
snacksList.add("巧克力");
snacksList.add("土豆片");
return snacksList;
}
/**
* 買零食(事實(shí)這些注釋都是不需要,只是為了大家理解)
* @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è)務(wù)異常,這里我只是用簡單的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;
}
/**
* 吃零食(事實(shí)這些注釋都是不需要,只是為了大家理解)
* @param snacks
*/
public List<String> eatSnacks(List<String> snacks){
for (String snack : snacks) {
System.out.println("一起吃" + snack);
}
return snacks;
}
/**
* 仍垃圾(事實(shí)這些注釋都是不需要,只是為了大家理解)
* @param litter
*/
public boolean dropLitter(List<String> litter){
boolean isDrop = false;
for(String snack:litter){
System.out.println("扔掉垃圾:" + snack);
}
/** 這里故意寫的有點(diǎn)冗余,只是方便大家學(xué)習(xí) **/
isDrop = true;
return isDrop;
}
}

讀書感悟
來自《窮查理智慧書》
- 忙碌的人們很少有閑客來訪;沸騰的鍋里絕不會落入蒼蠅。
- 失意時,沒有人了解;得意時,不了解自己。
- 憤怒總是有一定的理,但很少有好的道理。
- 你可能有時犯大錯,是因?yàn)槟阏J(rèn)為自己永遠(yuǎn)正確。
- 一百個小偷,也偷不走一個人的臉皮,特別是當(dāng)這個人沒有臉皮的話。
- 人們經(jīng)常把自己看錯了,卻很少把自己忘記了。
- 無人認(rèn)錯的爭吵必然延續(xù)無盡。
- 如果你的秘密不想被敵人知道,請先對你的朋友保密。
- 教訓(xùn)換來的一兩智慧,勝過書本一斤的知識。
其他
如果有帶給你一絲絲小快樂,就讓快樂繼續(xù)傳遞下去,歡迎轉(zhuǎn)載,點(diǎn)贊,頂,歡迎留下寶貴的意見,多謝支持!