天天看點

使用 Junit + Mockito 實踐單元測試

使用 Junit + Mockito 實踐單元測試

閱讀目錄

一、前言

二、JUnit 架構

三、Mockito 架構

回到頂部

相信做過開發的同學,都多多少少寫過下面的代碼,很長一段時間我一直以為這就是單元測試...

@SpringBootTest

@RunWith(SpringRunner.class)

public class UnitTest1 {

@Autowired
private UnitService unitService;

@Test
public void test() {
    System.out.println("----------------------");
    System.out.println(unitService.sayHello());
    System.out.println("----------------------");
}           

}

但這是單元測試嘛?unitService 中可能還依賴了 Dao 的操作;如果是微服務,可能還要起注冊中心。那麼這個“單元”也太大了吧!如果把它稱為內建測試,可能更恰當一點,那麼有沒有可能最小粒度進行單元測試嘛?

單元測試應該是一個帶有隔離性的功能測試。在單元測試中,應盡量避免其他類或系統的副作用影響。

單元測試的目标是一小段代碼,例如方法或類。方法或類的外部依賴關系應從單元測試中移除,而改為測試架構建立的 mock 對象來替換依賴對象。

單元測試一般由開發人員編寫,通過驗證或斷言目标的一些行為或狀态來達到測試的目的。

JUnit 是一個測試架構,它使用注解來辨別測試方法。JUnit 是 Github 上托管的一個開源項目。

一個 JUnit 測試指的是一個包含在測試類中的方法,要定義某個方法為測試方法,請使用 @Test 注解标注該方法。該方法執行被測代碼,可以使用 JUnit 或另一個 Assert 架構提供的 assert 方法來檢查預期結果與實際結果是否一緻,這些方法調用通常稱為斷言或斷言語句。

public class UnitTest2 {

@Test
public void test() {
    String sayHello = "Hello World";
    Assert.assertEquals("Hello World", sayHello);
}           

以下是一些常用的 JUnit 注解:

注解 描述

@Test 将方法辨別為測試方法

@Before 在每次測試之前執行。用于準備測試環境(例如,讀取輸入資料,初始化類)

@After 每次測試之後執行。用于清理測試環境(例如,删除臨時資料,恢複預設值)

@BeforeClass 用于 static方法,在所有測試開始之前執行一次。它用于執行耗時的活動,例如:連接配接到資料庫

@AfterClass 用于 static方法,在完成所有測試之後,執行一次。它用于執行清理活動,例如:與資料庫斷開連接配接

@Ignore 指定要忽略的測試

@Test(expected = Exception.class) 如果該方法未引發命名異常,則失敗

@Test(timeout=100) 如果該方法花費的時間超過100毫秒,則失敗

以下是一些常用的 Assert 斷言:

聲明 描述

fail([message]) 使方法失敗。在執行測試代碼之前,可用于檢查未到達代碼的特定部分或測試失敗

assertTrue([message,]布爾條件) 檢查布爾條件是否為真

assertFalse([message,]布爾條件) 檢查布爾條件是否為假

assertEquals([message,]預期,實際) 測試兩個值是否相同。注意:對于數組,會檢查引用而不是數組的内容

assertNull([message,]對象) 檢查對象是否為空

assertNotNull([message,]對象) 檢查對象是否不為空

assertSame([message,]預期,實際) 檢查兩個變量是否引用同一對象

assertNotSame([message,]預期,實際) 檢查兩個變量是否引用了不同的對象

從上面的介紹我們可以認識到,如何減少對外部的依賴才是實踐單元測試的關鍵。而這正是 Mockito 的使命,Mockito 是一個流行的 mock 架構,可以與 JUnit 結合使用,Mockito 允許我們建立和配置 mock 對象,使用 Mockito 将大大簡化了具有外部依賴項的類的測試開發。spring-boot-starter-test 中預設內建了 Mockito,不需要額外引入。

在測試中使用 Mockito,通常會:

mock 外部依賴關系并将 mock 對象插入待測代碼

執行被測代碼

驗證代碼是否正确執行

3.1 使用 Mockito 建立 mock 對象

Mockit o提供了幾種建立 mock 對象的方法:

使用靜态 mock() 方法

使用 @Mock 注解

如果使用 @Mock 注解,則必須觸發建立帶有 @Mock 注解的對象。使用 MockitoRule 可以做到,它通過調用靜态方法 MockitoAnnotations.initMocks(this) 來填充帶 @Mock 注解的字段。或者可以使用 @RunWith(MockitoJUnitRunner.class)。

public class UnitTest3 {

// 觸發建立帶有 @Mock 注解的對象
@Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
// 1. 使用 @Mock 注解建立 mock 對象
@Mock private UnitDao unitDao;

@Test
public void test() {
    // 2. 使用靜态 mock() 方法建立 mock 對象
    Iterator iterator = mock(Iterator.class);
    // when...thenReturn / doReturn...when 模拟依賴調用
    when(iterator.next()).thenReturn("hello");
    doReturn(1).when(unitDao).delete(anyLong());
    // 斷言
    Assert.assertEquals("hello", iterator.next());
    Assert.assertEquals(new Integer(1), unitDao.delete(1L));
}           

3.2 使用 mock 對象實踐單元測試

我們要單元測試的内容,常常包含着對資料庫的通路等等,那麼我們要如何 mock 掉這部分調用呢?我們可以使用 @InjectMocks 注解建立執行個體并使用 mock 對象進行依賴注入。

@Service

public class UnitServiceImpl implements UnitService {

@Autowired
private UnitDao unitDao;

@Override
public String sayHello() {
    Integer delete = unitDao.delete(1L);
    System.out.println(delete);
    return "hello unit";
}           

@RunWith(MockitoJUnitRunner.class)

@Mock
private UnitDao unitDao;
@InjectMocks
private UnitServiceImpl unitService;

@Test
public void unitTest() {
    // mock 調用
    when(unitDao.delete(anyLong())).thenReturn(1);
    Assert.assertEquals("hello unit", unitService.sayHello());
}           

Mockito 還有很多有趣的實踐,比如:@Spy或spy()方法、verify()驗證等等,鑒于篇幅原因,讀者可自行挖掘。

3.3 使用 PowerMock mock 靜态方法。

Mockito 也有一些局限性。例如:不能 mock 靜态方法和私有方法。有關詳細資訊,請參閱 Mockito限制的常見問題解答。這個時候我們就要用到 PowerMock,PowerMock 支援 JUnit 和 TestNG,擴充了 EasyMock 和 Mockito 架構,增加了mock static、final 方法的功能。

首先需要引入 PowerMock 的依賴:

<!-- PowerMock -->
    <dependency>
        <groupId>org.powermock</groupId>
        <artifactId>powermock-module-junit4</artifactId>
        <version>2.0.7</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.powermock</groupId>
        <artifactId>powermock-api-mockito2</artifactId>
        <version>2.0.7</version>
    </dependency>           

接下來就能愉快的 mock 靜态方法了。

@RunWith(PowerMockRunner.class)

@PrepareForTest({StringUtils.class})

public class UnitTest4 {

@Test
public void test() {
    mockStatic(StringUtils.class);
    when(StringUtils.getFilename(anyString())).thenReturn("localhost");
    Assert.assertEquals("localhost", StringUtils.getFilename(""));
}           

原文位址

https://www.cnblogs.com/jmcui/p/12802099.html