1. 程式人生 > >springboot 之單元測試:MockBean

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);//比較是否一致,為了判斷和期望值是否一致
    }
}

這樣應該就可以完成一個簡單的單元測試了,這些都只是虛擬碼,其實單元測試有很多的方法,也蠻有意思的,就是剛開始比較花時間。
其他的,後期有時間再寫詳細一點的。