天天看點

如何mock目前類的私有方法

背景

基礎知識

mockito單元測試:它的做法是mock掉目前類的所有外部依賴,保障自己的代碼沒有問題。舉個例子,如果資料庫查詢的語句出了問題,單元測試不會測試出來。因為它直接mock掉了,不會去真的去查資料庫。從這點來說,好像有點說不過去。但是吧,僅從代碼的角度來說,又好像并沒有什麼問題。因為它目标是保障自己的代碼正常,sql不算。再說除了單元測試,還測試用例不是。

編寫單元測試的流程:寫單元測試的時候,需要逐行分析代碼。如果是外部依賴,那麼mock掉它,自己模拟一個結果作為替代。否則繼續分析下一行,直到代碼結束。最後校驗在指定的輸入下,輸出的結果是否符合預期。

為什麼需要mock目前類的私有方法?

首先,當我們自動生成單元測試類的時候,它隻會為我們建立公共方法的測試方法。

如何mock目前類的私有方法

當被測試類的一個公共方法調用它的私有方法時,單元測試就需要為私有方法裡面的内容,也進行逐行分析mock。特别的,如果有另一個公共方法也調用了這個私有方法,那個這個公共方法也需要做同樣的事情。明顯,從測試覆寫來說,這個私有方法已經被覆寫,沒必要再逐行mock一次。這時候,如果能mock掉這個私有方法就好了,對吧!

舉個例子

建立被測試類

調用的私有方法分為“有傳回”和“沒有傳回”兩種。分别建立了pubFunction1調用帶傳回的私有方法,pubFunction2調用不帶傳回的私有方法。

package com.aliyu.service.demo.mock;

/**
 * 描述:單元測試如何mock目前類的其他私有方法調用
 * <p>作者: aliyu
 * <p>建立時間: 2023-01-10 10:57 下午
 */
public class MockitoDemo {

    /**
     * 模拟一個公共方法調用"不帶傳回"的私有方法
     */
    public void pubFunction1(){
        System.out.println("開始調用公共方法");
        withoutReturn();
        System.out.println("結束調用公共方法");
    }
    /**
     * 模拟一個公共方法調用"帶傳回"的私有方法
     */
    public void pubFunction2(){
        System.out.println("開始調用公共方法");
        withReturn();
        System.out.println("結束調用公共方法");
    }

    private void withoutReturn(){
        System.out.println("調用了'不帶'傳回的私有方法");
    }

    private String withReturn(){
        System.out.println("調用了'帶'傳回的私有方法");
        return "AAA";
    }
}
           

建立測試類

不帶傳回的私有方法mock

如何mock目前類的私有方法

注:還需要注意的是,必須是junit4,否則會報錯。在後面會提到。

完整代碼如下:

package com.aliyu.service.demo.mock;

import org.junit.Test;
import org.junit.jupiter.api.BeforeEach;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

/**
 * 描述:單元測試如何mock目前類的其他私有方法調用
 * <p>作者: aliyu
 * <p>建立時間: 2023-01-11 1:40 上午
 */
@RunWith(PowerMockRunner.class)
@PrepareForTest(MockitoDemo.class)
public class MockitoDemoTest {

    @InjectMocks
    private MockitoDemo mockitoDemo;

    @BeforeEach
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
    }

    /**
     * 測試公共方法調用不帶傳回的私有方法
     * doNothing實作(暫不清楚和doAnswer的差別,傾向于使用doNothing)
     */
    @Test
    public void testPubFunction1() {
        MockitoDemo mockitoDemo1 = PowerMockito.spy(mockitoDemo);
        try {
            PowerMockito.doNothing().when(mockitoDemo1,"withoutReturn");
        } catch (Exception e) {
            e.printStackTrace();
        }
        mockitoDemo1.pubFunction1();
    }

    /**
     * 測試公共方法調用不帶傳回的私有方法
     * doAnswer實作
     */
    @Test
    public void testPubFunction3() {
        MockitoDemo mockitoDemo1 = PowerMockito.spy(mockitoDemo);
        try{
            Answer answer=new Answer(){
                public Object answer(InvocationOnMock invocationOnMock) throws Throwable{
                    return null;
                }
            };
            PowerMockito.doAnswer(answer).when(mockitoDemo1,"withoutReturn");
        }catch(Exception e){
            e.printStackTrace();
        }
        mockitoDemo1.pubFunction1();
    }

}
           

執行檢視結果:

如何mock目前類的私有方法

注:可以看到沒有輸出"調用了’不帶’傳回的私有方法"字樣,說明已經成功mock。

帶傳回的私有方法mock

與不帶傳回的私有方法差别隻在于mock時用的方法。

如何mock目前類的私有方法

執行檢視結果:

如何mock目前類的私有方法

注:可以看到沒有輸出"調用了’帶’傳回的私有方法"字樣,說明已經成功mock。

其他

如果用不是junit4,而是junit5會報錯
如何mock目前類的私有方法

大緻的意思就是公有方法沒有傳回值,不能打一個有傳回值的樁。明明我是為有傳回的私有方法打樁的咧。

帶傳回值時doReturn必須在前面,否則mock失敗

如何mock目前類的私有方法

注:就好像如果不行doReturn聲明傳回值的話,它就會先去執行一下私有方法。。