springboot 之單元測試:MockBean
程式碼寫到一半,之前的程式碼只需要完成功能且對其進行優化就完事了,就在這時,突然要我加上單元測試,崩潰啊……
我也只能把它啃了。
原諒我只做過簡單Java程式的單元測試,是使用Junit去Test的。
現在要使用SpringBoot和Junit去弄,好吧,我也不太懂什麼是單元測試,所以就看看吧:
摘自百度百科:
單元測試(模組測試)是開發者編寫的一小段程式碼,用於檢驗被測程式碼的一個很小的、很明確的功能是否正確。
一個單元測試是用於判斷某個特定條件(或者場景)下某個特定函式的行為。
總的來說,就是為了後期做程式碼優化的時候,用來判斷優化後的程式碼是否可以得到優化前的程式碼執行結果。如果可以得到,那就是程式碼優化的很好。如果得不到,那就是優化的程式碼存在問題,需要進一步的深入查詢問題,並且解決。這就是進行單元測試的好處。
有點歸總:
進行充分的單元測試,是提高軟體質量,降低開發成本的必由之路。
對於程式設計師來說,如果養成了對自己寫的程式碼進行單元測試的習慣,不但可以寫出高質量的程式碼,而且還能提高程式設計水平。
要進行充分的單元測試,應專門編寫測試程式碼,並與產品程式碼隔離。
1、它是一種驗證行為。
程式中的每一項功能都是測試來驗證它的正確性。它為以後的開發提供支援。就算是開發後期,我們也可以輕鬆的增加功能或更改程式結構,而不用擔心這個過程中會破壞重要的東西。而且它為程式碼的重構提供了保障。這樣,我們就可以更自由的對程式進行改進。
2、它是一種設計行為。
編寫單元測試將使我們從呼叫者觀察、思考。特別是先寫測試(test-first),迫使我們把程式設計成易於呼叫和可測試的,即迫使我們解除軟體中的耦合。
3、它是一種編寫文件的行為。
單元測試是一種無價的文件,它是展示函式或類如何使用的最佳文件。這份文件是可編譯、可執行的,並且它保持最新,永遠與程式碼同步。
4、它具有迴歸性。
自動化的單元測試避免了程式碼出現迴歸,編寫完成之後,可以隨時隨地的快速執行測試。
… …
使用的是spring boot + Junit;
下面我們來使用一個例子,查詢使用者資訊:
@Service
public class Test1 {
@Autowired
private CheckCode checkCode;//資料校驗
@Autowired
private Redis redis;//新增資料
@Autowired
private DaoService daoService;//dao層,,資料庫訪問層
//查詢使用者資訊
public List<Object> query(String code){
boolean flag = checkCode.isFlag(code);//資料校驗
List<Object> listData = new ArrayList<>();
if(flag){
listData = daoService.queryUserInfo();//訪問資料庫獲取資料
}else{
//......
}
/**
*重要的程式碼邏輯
*/
//略
redis.set(JSON.toJSONString(listData));//新增資料到redis,無返回值
return listData;
}
}
我們正常情況下:執行query(String code)這個方法,都會呼叫到CheckCode、DaoService 這兩個類或者介面的方法,對於資料校驗和資料庫訪問,假如我們不用考慮其對程式碼的影響,那麼我們的單元測試可以這麼寫
//忘了加單元測試的註解了
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class)
@WebAppConfiguration
public class Test2 {
@Autowired
private Test1 test;//這是我們一會要進行單元測試的類
//對兩個不需要驗證的類進行mock
@MockBean
private CheckCode checkCode;//資料校驗
@MockBean
private DaoService daoService;//dao層,,資料庫訪問層
//對於不需要返回的任何值的類的所有方法,可以直接使用MockBean,直接忽略,如
@MockBean
private Redis redis;
//這樣寫,只要是使用到redis這個物件的地方,都不會執行。直接跳過
//第一種操作
//為方便使用,可以使用初始化形式mock
//公有的方法可以這樣
@Before
public void init(){
//這句程式碼的意思是:當執行到checkCode.isFlag(Mockito.anyString())方法時,返回:true
//Mockito.anyString():任意一個String物件
//這中方式可以將返回的值和測試程式碼分開,我覺得比較美觀
Mockito.when(checkCode.isFlag(Mockito.anyString())).thenReturn(true);
//也可以使用這種
//兩種方式得到的結果是一樣的
Mockito.when(checkCode.isFlag(Mockito.anyString())).thenAnswer(new Answer<boolean>() {
@Override
public boolean answer(InvocationOnMock invocation) throws Throwable {
//給出預設值
boolean flag = true;
//返回
return flag;
}});
//兩種方法都是是的isFlag(String)這個方法返回預設的值:true。
//需要特別注意的是,不能帶一個方法或者類mock兩次及其以上
}
//單元測試的方法必須是公有的(public)、沒有返回值的(void)、沒有入參的()
@Test
public void test1(){
Mockito.when(daoService.queryUserInfo()).thenAnswer(new Answer<List<Object>>() {
@Override
public List<Object> answer(InvocationOnMock invocation) throws Throwable {
List<Object> listData = new ArrayList<>();
//對listData賦值
return listData;
}});
List<Object> data = test.query(anyString);//返回的值是上邊的mock的預設值
Assert.assertEquals(arg1,arg2);//比較是否一致,為了判斷和期望值是否一致
}
}
這樣應該就可以完成一個簡單的單元測試了,這些都只是虛擬碼,其實單元測試有很多的方法,也蠻有意思的,就是剛開始比較花時間。
其他的,後期有時間再寫詳細一點的。