Mock 測試技術(shù)詳解及高級特性,你不得不會的技能!

Mock 是軟件測試中常用的一種技術(shù),它可以模擬外部依賴的行為和狀態(tài),以便進(jìn)行更全面、準(zhǔn)確和可靠的測試覆蓋。Java 中的 Mock 框架是一個功能強(qiáng)大、易用的工具,可以幫助開發(fā)者快速、輕松地創(chuàng)建和配置 Mock 對象,并支持各種靈活的測試場景和需求。

本文將詳細(xì)介紹 Java 中的 Mock 測試技術(shù),包括 Mock 框架的基本概念和使用方法、Mockito 框架的高級特性和擴(kuò)展技巧等內(nèi)容。

一、Mock 測試的基本概念和原理

1.1 Mock 測試的定義和目的

Mock 測試是一種軟件測試技術(shù),它可以模擬外部依賴組件的行為和狀態(tài),以便進(jìn)行獨(dú)立、穩(wěn)定和快速的測試。在實(shí)際軟件開發(fā)中,我們常常需要依賴其他組件或服務(wù)來完成某些業(yè)務(wù)邏輯,例如數(shù)據(jù)庫、網(wǎng)絡(luò)連接、消息隊(duì)列等等。這些外部依賴可能存在各種問題,例如不穩(wěn)定、缺乏數(shù)據(jù)、難以模擬等等,從而影響我們對代碼邏輯的測試、調(diào)試和優(yōu)化。

Mock 測試可以解決這些問題,它通過構(gòu)造和配置 Mock 對象來模擬外部依賴的行為和狀態(tài),從而使得測試變得獨(dú)立、穩(wěn)定和準(zhǔn)確。Mock 測試的目的是隔離被測代碼和外部依賴,消除隨機(jī)性和不可控性,提高測試覆蓋和質(zhì)量,加速軟件開發(fā)和迭代。

1.2 Mock 測試的原理和實(shí)現(xiàn)方式

Mock 測試的核心原理是基于模擬對象(Mock Object)的概念來實(shí)現(xiàn)的。模擬對象是一種特殊的對象,它可以模擬外部組件的行為和狀態(tài),以便進(jìn)行測試。在 Java 中,我們可以使用 Mock 框架來創(chuàng)建和配置模擬對象,并使用相關(guān) API 和工具進(jìn)行斷言和驗(yàn)證。

在實(shí)現(xiàn)時(shí),Mock 測試通常分為三個階段:定義 Mock 對象、設(shè)置 Mock 行為、執(zhí)行測試和驗(yàn)證結(jié)果。首先,我們需要定義需要 Mock 的對象,并使用 Mock 框架的 API 創(chuàng)建 Mock 對象。其次,我們需要設(shè)置 Mock 對象的行為,包括返回值、拋出異常、執(zhí)行邏輯等等。最后,我們通過測試代碼調(diào)用 Mock 對象,并使用斷言和驗(yàn)證方法來驗(yàn)證測試結(jié)果是否符合預(yù)期。

Mock 測試的實(shí)現(xiàn)方式主要有兩種:手動編碼和自動化工具。手動編碼需要程序員自己編寫 Mock 對象和相關(guān)代碼,相對較為復(fù)雜和繁瑣;而自動化工具可以幫助程序員快速、輕松地創(chuàng)建和配置 Mock 對象,并提供豐富的 API 和工具,例如 Mockito 等。

二、Java 中的 Mock 框架

2.1 Mock 框架的分類和特點(diǎn)

Java 中存在多種 Mock 框架,它們各有特點(diǎn)和適用場景。根據(jù)實(shí)現(xiàn)方式和功能特性,Java 中的 Mock 框架可以分為手動編碼 Mock 和自動化 Mock 兩類。

手動編碼 Mock 是指程序員通過手動編寫 Mock 對象和相關(guān)代碼實(shí)現(xiàn)外部依賴的模擬,例如使用匿名內(nèi)部類或樁對象等方式。手動編碼 Mock 的優(yōu)點(diǎn)是靈活、可控,可以滿足各種特定的測試需求,但缺點(diǎn)是編碼繁瑣、維護(hù)成本高、可讀性差等。

自動化 Mock 是指使用自動化工具和庫來創(chuàng)建和配置 Mock 對象,例如 Mockito、EasyMock、PowerMock 等。自動化 Mock 的優(yōu)點(diǎn)是方便、易用,可以大幅降低編碼工作量和技術(shù)難度,同時(shí)提供豐富的 API 和工具支持,可以輕松應(yīng)對各種復(fù)雜測試場景和需求。

在自動化 Mock 框架中,Mockito 是一種主流和優(yōu)秀的測試工具,它提供了豐富的 Mock API 和工具,可以幫助程序員快速、輕松地創(chuàng)建和配置 Mock 對象,并支持各種靈活的測試場景和需求。下面我們將重點(diǎn)介紹 Mockito 框架的使用方法和高級特性。

2.2 Mockito 框架的基本用法

Mockito 框架提供了一系列的靜態(tài)方法和類來創(chuàng)建和配置 Mock 對象,這些方法包括 mock()、when()、thenReturn()、any() 等等。在使用 Mockito 框架時(shí),我們需要遵循以下幾個基本步驟:

(1)創(chuàng)建 Mock 對象:使用 mock() 方法創(chuàng)建 Mock 對象,并指定需要 Mock 的類或接口。例如:

List<String> list = mock(List.class);

(2)設(shè)置 Mock 行為:使用 when() 方法設(shè)置 Mock 對象的行為,并使用 thenReturn()、thenThrow() 等方法指定返回值或異常。例如:

when(list.get(0)).thenReturn("mock");
when(list.size()).thenThrow(new RuntimeException());

(3)調(diào)用 Mock 對象:在測試代碼中調(diào)用 Mock 對象的方法,并進(jìn)行斷言和驗(yàn)證。例如:

assertEquals("mock", list.get(0));
verify(list, times(1)).get(0);

需要注意的是,在使用 Mockito 框架時(shí),我們應(yīng)該考慮方法的訪問修飾符和參數(shù)類型等因素,并盡可能保持被 Mock 對象的行為和狀態(tài)與實(shí)際場景一致。

同時(shí),Mockito 框架還提供了 Mock 注解、Mockito 插件和高級特性等功能,以幫助程序員更好地進(jìn)行測試和開發(fā)。下面我們將詳細(xì)介紹這些內(nèi)容。

三、Mockito 框架的高級特性

3.1 Mockito 注解的使用

Mockito 框架提供了多種注解來簡化測試代碼的編寫和維護(hù),包括 @Mock、@InjectMocks、@Spy、@Captor 等等。這些注解可以幫助程序員自動創(chuàng)建和配置 Mock 對象,并解決 Mock 依賴和單元測試等問題。

(1)@Mock 注解

@Mock 注解用于指示需要 Mock 的類或接口,并自動創(chuàng)建 Mock 對象。在使用 @Mock 注解時(shí),我們需要使用 MockitoJUnitRunner 或 MockitoAnnotations.initMocks() 方法來初始化 Mock 對象。

例如:

@RunWith(MockitoJUnitRunner.class)
public class MyServiceTest {
    @Mock
    private MyDao myDao;

    @InjectMocks
    private MyServiceImpl myService;

    @Test
    public void testDoSomething() {
        when(myDao.getData()).thenReturn("mock");
        String result = myService.doSomething();
        assertEquals("mock", result);
    }
}

上面的代碼中,我們使用 @Mock 注解指示需要 Mock 的 MyDao 接口,并使用 MockitoJUnitRunner 或 MockitoAnnotations.initMocks() 方法初始化 Mock 對象。接著,我們使用 @InjectMocks 注解指示需要注入的 MyServiceImpl 類,并在測試方法中設(shè)置 Mock 的行為和執(zhí)行邏輯。

(2)@InjectMocks 注解

@InjectMocks 注解用于指示需要進(jìn)行依賴注入的類,以便使用 Mock 對象。在使用 @InjectMocks 注解時(shí),我們需要同時(shí)使用 @Mock 或 @Spy 注解來創(chuàng)建 Mock 對象或 Spy 對象。

例如:

@RunWith(MockitoJUnitRunner.class)
public class MyServiceTest {
    @Mock
    private MyDao myDao;

    @Spy
    private MyUtils myUtils;

    @InjectMocks
    private MyServiceImpl myService;

    @Test
    public void testDoSomething() {
        when(myDao.getData()).thenReturn("mock");
        doReturn("stubbed").when(myUtils).doSomething();
        String result = myService.doSomething();
        assertEquals("mockstubbed", result);
    }
}

上面的代碼中,我們使用 @Mock 和 @Spy 注解創(chuàng)建 MyDao 和 MyUtils 的 Mock 對象和 Spy 對象,并使用 @InjectMocks 注解指示需要注入的 MyServiceImpl 類。接著,我們使用 when() 和 doReturn() 方法指定 Mock 對象和 Spy 對象的行為和返回值,并在測試方法中進(jìn)行斷言和驗(yàn)證。

(3)@Spy 注解

@Spy 注解用于指示需要進(jìn)行部分模擬的對象,并自動創(chuàng)建 Spy 對象。Spy 對象可以保留對象的原有狀態(tài)和方法實(shí)現(xiàn),并提供相關(guān)的函數(shù)和驗(yàn)證功能。

例如:

@RunWith(MockitoJUnitRunner.class)
public class MyServiceTest {
    @Spy
    private MyUtils myUtils;

    @InjectMocks
    private MyServiceImpl myService;

    @Test
    public void testDoSomething() {
        doReturn("stubbed").when(myUtils).doSomething();
        String result = myService.doSomething();
        assertEquals("realstubbed", result);
    }
}

上面的代碼中,我們使用 @Spy 注解創(chuàng)建 MyUtils 類的 Spy 對象,并使用 @InjectMocks 注解指示需要注入的 MyServiceImpl 類。接著,我們使用 doReturn() 方法指定 Spy 對象的行為和返回值,并在測試方法中進(jìn)行斷言和驗(yàn)證。

(4)@Captor 注解

@Captor 注解用于捕獲 Mock 對象的參數(shù)值,以便進(jìn)一步處理和驗(yàn)證。在使用 @Captor 注解時(shí),我們需要使用 ArgumentCaptor 類來實(shí)例化 Captor 對象,并使用 when() 方法設(shè)置 Mock 對象的行為和返回值。

例如:

@RunWith(MockitoJUnitRunner.class)
public class MyServiceTest {
    @Mock
    private MyDao myDao;

    @InjectMocks
    private MyServiceImpl myService;

    @Captor
    private ArgumentCaptor<String> captor;

    @Test
    public void testDoSomethingWithArgument() {
        when(myDao.getData(anyString())).thenReturn("mock");
        String result = myService.doSomethingWithArgument("test");
        assertEquals("mock", result);
        verify(myDao).getData(captor.capture());
        assertEquals("test", captor.getValue());
    }
}

上面的代碼中,我們使用 @Mock 注解創(chuàng)建需要 Mock 的 MyDao 對象,并使用 @InjectMocks 注解注入 MyServiceImpl 對象。接著,我們使用 @Captor 注解創(chuàng)建 ArgumentCaptor 對象來捕獲 Mock 方法的參數(shù)值,并在測試方法中使用 when() 方法設(shè)置 Mock 方法的返回值。最后,我們在測試方法中調(diào)用 Mock 方法,并使用 verify() 方法來驗(yàn)證 Mock 方法是否被調(diào)用,并使用 getValue() 方法來獲取 Captor 對象的參數(shù)值。

3.2 Mockito 插件的使用

Mockito 框架通過插件機(jī)制來擴(kuò)展功能和提供新特性,例如 Hamcrest、JUnit、JUnit5 等插件。這些插件可以幫助程序員更好地進(jìn)行測試和開發(fā),并提供方便的 API 和工具支持。

(1)Hamcrest 插件

Hamcrest 插件是一個框架,它提供了豐富的 Matcher 類和 API,可以幫助程序員方便地進(jìn)行對象匹配和斷言。在使用 Hamcrest 插件時(shí),我們需要使用 assertThat() 方法來進(jìn)行匹配和斷言。

例如:

@RunWith(MockitoJUnitRunner.class)
public class MyServiceTest {
    @Mock
    private MyDao myDao;

    @InjectMocks
    private MyServiceImpl myService;

    @Test
    public void testDoSomethingWithMatcher() {
        when(myDao.getData(anyString())).thenReturn("mock");
        String result = myService.doSomethingWithArgument("test");
        assertThat(result, is("mock"));
    }
}

上面的代碼中,我們使用 @Mock 注解創(chuàng)建需要 Mock 的 MyDao 對象,并使用 @InjectMocks 注解注入 MyServiceImpl 對象。接著,我們使用 when() 方法設(shè)置 Mock 方法的返回值,并在測試方法中調(diào)用 doSomethingWithArgument() 方法,并使用 assertThat() 方法進(jìn)行匹配和斷言。

(2)JUnit 插件

JUnit 插件是一個測試框架,它提供了多種測試 API 和工具,包括 Assert、Assume、Parameterized 等類和注解。在使用 JUnit 插件時(shí),我們需要遵循 JUnit 的測試規(guī)范和標(biāo)準(zhǔn),編寫符合要求的測試用例。

例如:

@RunWith(MockitoJUnitRunner.class)
public class MyServiceTest {
    @Mock
    private MyDao myDao;

    @InjectMocks
    private MyServiceImpl myService;

    @Test
    public void testDoSomethingWithAssert() {
        when(myDao.getData(anyString())).thenReturn("mock");
        String result = myService.doSomethingWithArgument("test");
        assertNotNull(result);
        assertTrue(result.startsWith("m"));
    }
}

上面的代碼中,我們使用 @Mock 注解創(chuàng)建需要 Mock 的 MyDao 對象,并使用 @InjectMocks 注解注入 MyServiceImpl 對象。接著,我們使用 when() 方法設(shè)置 Mock 方法的返回值,并在測試方法中調(diào)用 doSomethingWithArgument() 方法,并使用 JUnit 的 Assert 類進(jìn)行斷言。

(3)JUnit5 插件

JUnit5 插件是 JUnit4 之后的版本,它提供了更豐富、更強(qiáng)大的測試 API 和工具,例如 Test、BeforeAll、AfterAll、ParameterizedTest、Assertions、DisplayName 等注解和接口。在使用 JUnit5 插件時(shí),我們需要遵循 JUnit5 的測試規(guī)范和標(biāo)準(zhǔn),編寫符合要求的測試用例。

例如:

@DisplayName("MyServiceTest")
public class MyServiceTest {
    @Mock
    private MyDao myDao;

    @InjectMocks
    private MyServiceImpl myService;

    @Test
    @DisplayName("Test doSomethingWithArgument")
    void testDoSomethingWithArgument() {
        when(myDao.getData(anyString())).thenReturn("mock");
        String result = myService.doSomethingWithArgument("test");
        assertEquals("mock", result);
    }

    @Test
    @DisplayName("Test doSomethingWithNullArgument")
    void testDoSomethingWithNullArgument() {
        when(myDao.getData(null)).thenReturn("null");
        String result = myService.doSomethingWithArgument(null);
        assertEquals("null", result);
    }
}

上面的代碼中,我們使用 @Mock 注解創(chuàng)建需要 Mock 的 MyDao 對象,并使用 @InjectMocks 注解注入 MyServiceImpl 對象。接著,我們使用 when() 方法設(shè)置 Mock 方法的返回值,并在測試方法中調(diào)用 doSomethingWithArgument() 方法,并使用 JUnit5 的 Assertions 類進(jìn)行斷言,同時(shí)使用 DisplayName 注解來標(biāo)識測試用例的名稱和描述。

3.3 Mockito 擴(kuò)展技巧

Mockito 框架提供了多種擴(kuò)展技巧,可以幫助程序員更好地進(jìn)行測試和開發(fā),例如策略模式、Spy 對象、連續(xù)調(diào)用等技巧。

(1)策略模式

策略模式是一種設(shè)計(jì)模式,它可以將算法的實(shí)現(xiàn)與調(diào)用解耦,從而提高代碼的靈活性和可復(fù)用性。在使用 Mockito 框架時(shí),我們可以使用策略模式來實(shí)現(xiàn) Mock 對象的行為和邏輯,從而更好地進(jìn)行測試和開發(fā)。

例如:

interface MyStrategy {
    String doSomething();
}

class MyStrategyImpl implements MyStrategy {
    @Override
    public String doSomething() {
        return "real";
    }
}

class MyService {
    private MyStrategy strategy;

    public MyService(MyStrategy strategy) {
        this.strategy = strategy;
    }

    public String doSomething() {
        return strategy.doSomething();
    }
}

@RunWith(MockitoJUnitRunner.class)
public class MyServiceTest {
    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
    private MyStrategy myStrategy;

    private MyService myService;

    @Before
    public void setUp() throws Exception {
        myService = new MyService(myStrategy);
    }

    @Test
    public void testDoSomethingWithMock() {
        when(myStrategy.doSomething()).thenReturn("mock");
        String result = myService.doSomething();
        assertEquals("mock", result);
    }

    @Test
    public void testDoSomethingWithReal() {
        MyStrategy realStrategy = new MyStrategyImpl();
        MyService realService = new MyService(realStrategy);
        String result = realService.doSomething();
        assertEquals("real", result);
    }
}

上面的代碼中,我們定義了一個 MyStrategy 接口和一個 MyStrategyImpl 實(shí)現(xiàn)類,用于表示算法的具體實(shí)現(xiàn)。接著,我們定義了一個 MyService 類,構(gòu)造方法中傳入 MyStrategy 接口,用于執(zhí)行具體的算法實(shí)現(xiàn)。然后,我們在測試類中通過 @Mock 注解創(chuàng)建 Mock 的 MyStrategy 對象,并使用策略模式將其注入到 MyService 中。最后,在測試方法中使用 when() 方法設(shè)置 Mock 對象的返回值,并調(diào)用 MyService 的 doSomething() 方法進(jìn)行測試和斷言。

(2)Spy 對象

Spy 對象是 Mockito 框架中的一種機(jī)制,它可以對真實(shí)對象進(jìn)行部分 Mock,可以幫助程序員更好地進(jìn)行測試和調(diào)試。在使用 Spy 對象時(shí),我們需要使用 Mockito.spy() 方法創(chuàng)建 Spy 對象,并在測試方法中使用 doReturn()、doThrow() 等方法設(shè)置 Spy 對象的行為和返回值。

例如:

class MyServiceImpl implements MyService {
    private MyDao myDao;

    public MyServiceImpl(MyDao myDao) {
        this.myDao = myDao;
    }

    @Override
    public String doSomethingWithArgument(String argument) {
        if (argument == null) {
            throw new IllegalArgumentException();
        }
        return myDao.getData(argument);
    }
}

@RunWith(MockitoJUnitRunner.class)
public class MyServiceTest {
    private MyDao myDao = Mockito.mock(MyDao.class);

    private MyService spyService = new MyServiceImpl(myDao);

    @Test(expected = IllegalArgumentException.class)
    public void testDoSomethingWithNullArgument() {
        spyService = Mockito.spy(spyService);
        doThrow(new IllegalArgumentException()).when(spyService).doSomethingWithArgument(null);
        spyService.doSomethingWithArgument(null);
    }
}

上面的代碼中,我們創(chuàng)建了一個 MyServiceImpl 類和一個 MyDao 接口,并實(shí)現(xiàn)了 MyService 的 doSomethingWithArgument() 方法。接著,我們使用 @Mock 注解創(chuàng)建需要 Mock 的 MyDao 對象,并使用構(gòu)造方法注入到 MyServiceImpl 中,然后將 MyServiceImpl 對象賦值給 SpyService 對象。在測試方法中,我們使用 Mockito.spy() 方法創(chuàng)建 Spy 對象,并使用 doThrow() 方法設(shè)置 Spy 對象在接收到 null 參數(shù)時(shí)拋出 IllegalArgumentException 異常。

(3)連續(xù)調(diào)用

連續(xù)調(diào)用是 Mockito 框架中的一個機(jī)制,它可以幫助程序員在 Mock 對象的方法調(diào)用鏈中進(jìn)行多次設(shè)置和斷言,從而更好地進(jìn)行測試和驗(yàn)證。在使用連續(xù)調(diào)用時(shí),我們需要使用 thenReturn()、thenThrow() 等方法來設(shè)置連續(xù)調(diào)用的返回值或異常,并在測試方法中使用 verify()、verifyNoMoreInteractions() 等方法進(jìn)行斷言和驗(yàn)證。

例如:

class MyServiceImpl implements MyService {
    private MyDao myDao;

    public MyServiceImpl(MyDao myDao) {
        this.myDao = myDao;
    }

    @Override
    public String doSomethingWithArgument(String argument) {
        if (argument == null) {
            throw new IllegalArgumentException();
        }
        return myDao.getData(argument);
    }
}

@RunWith(MockitoJUnitRunner.class)
public class MyServiceTest {
    private MyDao myDao = Mockito.mock(MyDao.class);

    private MyService myService = new MyServiceImpl(myDao);

    @Test
    public void testDoSomethingWithMultipleCalls() {
        when(myDao.getData(anyString()))
                .thenReturn("first")

                .thenThrow(new RuntimeException())

                .thenReturn("second");

        assertEquals("first", myService.doSomethingWithArgument("test"));
        try {
            myService.doSomethingWithArgument(null);
        } catch (IllegalArgumentException e) {
            // ignore
        }
        assertNull(myService.doSomethingWithArgument("null"));
        assertEquals("second", myService.doSomethingWithArgument("test"));
        verify(myDao, times(4)).getData(anyString());
        verifyNoMoreInteractions(myDao);
    }
}

上面的代碼中,我們創(chuàng)建了一個 MyServiceImpl 類和一個 MyDao 接口,并實(shí)現(xiàn)了 MyService 的 doSomethingWithArgument() 方法。接著,我們使用 @Mock 注解創(chuàng)建需要 Mock 的 MyDao 對象,并使用構(gòu)造方法注入到 MyServiceImpl 中,然后創(chuàng)建 MyServiceImpl 對象。在測試方法中,我們使用 when() 方法進(jìn)行連續(xù)調(diào)用設(shè)置,并使用 try-catch 語句捕獲指定方法的異常。最后,我們在測試方法中使用 verify()、verifyNoMoreInteractions() 等方法進(jìn)行斷言和驗(yàn)證。

(4)參數(shù)匹配器

參數(shù)匹配器是 Mockito 框架中的一個機(jī)制,它可以幫助程序員在設(shè)置 Mock 對象的方法時(shí),使用特定的參數(shù)匹配規(guī)則來匹配方法調(diào)用的實(shí)際參數(shù)。在使用參數(shù)匹配器時(shí),我們需要使用 any()、eq() 等方法來設(shè)置參數(shù)匹配器,并在測試方法中使用 verify()、verifyNoMoreInteractions() 等方法進(jìn)行斷言和驗(yàn)證。

例如:

class MyServiceImpl implements MyService {
    private MyDao myDao;

    public MyServiceImpl(MyDao myDao) {
        this.myDao = myDao;
    }

    @Override
    public String doSomethingWithArgument(String argument) {
        if (argument == null) {
            throw new IllegalArgumentException();
        }
        return myDao.getData(argument);
    }
}

@RunWith(MockitoJUnitRunner.class)
public class MyServiceTest {
    private MyDao myDao = Mockito.mock(MyDao.class);

    private MyService myService = new MyServiceImpl(myDao);

    @Test
    public void testDoSomethingWithArgumentMatcher() {
        when(myDao.getData(anyString())).thenReturn("mock");
        String result = myService.doSomethingWithArgument("test");
        assertEquals("mock", result);
        verify(myDao, times(1)).getData(anyString());
        verifyNoMoreInteractions(myDao);
    }

    @Test(expected = IllegalArgumentException.class)
    public void testDoSomethingWithArgumentNullMatcher() {
        myService.doSomethingWithArgument(null);
    }
}

上面的代碼中,我們創(chuàng)建了一個 MyServiceImpl 類和一個 MyDao 接口,并實(shí)現(xiàn)了 MyService 的 doSomethingWithArgument() 方法。接著,我們使用 @Mock 注解創(chuàng)建需要 Mock 的 MyDao 對象,并使用構(gòu)造方法注入到 MyServiceImpl 中,然后創(chuàng)建 MyServiceImpl 對象。在測試方法中,我們使用 anyString() 方法設(shè)置參數(shù)匹配器,并使用 when() 方法進(jìn)行 Mock 設(shè)置。最后,我們在測試方法中使用 verify()、verifyNoMoreInteractions() 等方法進(jìn)行斷言和驗(yàn)證。

(5)MockitoAnnotations 和 InjectMocks

MockitoAnnotations 和 InjectMocks 是 Mockito 框架中的兩個注解,它們可以幫助程序員更輕松地創(chuàng)建 Mock 對象和將 Mock 對象注入到需要測試的對象中。在使用 MockitoAnnotations 和 InjectMocks 時(shí),我們需要在測試類中使用這兩個注解,并在需要創(chuàng)建 Mock 對象和注入 Mock 對象的屬性和方法上使用 @Mock 和 @InjectMocks 注解。

例如:

class MyServiceImpl implements MyService {
    private MyDao myDao;

    public MyServiceImpl(MyDao myDao) {
        this.myDao = myDao;
    }

    @Override
    public String doSomethingWithArgument(String argument) {
        if (argument == null) {
            throw new IllegalArgumentException();
        }
        return myDao.getData(argument);
    }
}

@RunWith(MockitoJUnitRunner.class)
public class MyServiceTest {
    @Mock
    private MyDao myDao;

    @InjectMocks
    private MyService myService = new MyServiceImpl(myDao);

    @Test
    public void testDoSomethingWithArgument() {
        when(myDao.getData(anyString())).thenReturn("mock");
        String result = myService.doSomethingWithArgument("test");
        assertEquals("mock", result);
    }

    @Test(expected = IllegalArgumentException.class)
    public void testDoSomethingWithArgumentNull() {
        myService.doSomethingWithArgument(null);
    }
}

上面的代碼中,我們創(chuàng)建了一個 MyServiceImpl 類和一個 MyDao 接口,并實(shí)現(xiàn)了 MyService 的 doSomethingWithArgument() 方法。接著,我們在測試類中使用 @Mock 和 @InjectMocks 注解來創(chuàng)建 Mock 對象和將 Mock 對象注入到 MyServiceImpl 中,然后在測試方法中使用 when() 方法進(jìn)行 Mock 以及調(diào)用 MyService 的方法進(jìn)行測試和斷言。

(6)Mockito.reset()

Mockito.reset() 方法可以幫助程序員重置 Mock 對象的狀態(tài),并清除之前設(shè)置的 Mock 和 Stub。在進(jìn)行單元測試時(shí),我們有時(shí)候需要重置 Mock 對象的狀態(tài),以便在多個測試方法中反復(fù)使用同一個 Mock 對象。

例如:

java
class MyServiceImpl implements MyService {
    private MyDao myDao;

    public MyServiceImpl(MyDao myDao) {
        this.myDao = myDao;
    }

    @Override
    public String doSomethingWithArgument(String argument) {
        if (argument == null) {
            throw new IllegalArgumentException();
        }
        return myDao.getData(argument);
    }
}

@RunWith(MockitoJUnitRunner.class)
public class MyServiceTest {
    private MyDao myDao = Mockito.mock(MyDao.class);

    private MyService myService = new MyServiceImpl(myDao);

    @Test
    public void testDoSomethingWithArgument() {
        when(myDao.getData(anyString())).thenReturn("mock");
        String result = myService.doSomethingWithArgument("test");
        assertEquals("mock", result);
        verify(myDao, times(1)).getData(anyString());
    }

    @Test
    public void testDoSomethingWithArgumentReset() {
        Mockito.reset(myDao);
        String result = myService.doSomethingWithArgument("test");
        assertNull(result);
        verify(myDao, times(1)).getData(anyString());
    }
}

上面的代碼中,我們在第一個測試方法中設(shè)置了 Mock 和 Stub,并進(jìn)行了測試和驗(yàn)證。在第二個測試方法中,我們使用 Mockito.reset() 方法重置了 MyDao 的狀態(tài),然后再次調(diào)用 MyService 的 doSomethingWithArgument() 方法進(jìn)行測試,并驗(yàn)證對 MyDao 的調(diào)用次數(shù)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容