Mockito結合TestNG快速入門
什么是Mockito
Mockito 是一個專門用于 Java 的強大測試框架,主要用來創建和管理模擬對象,輔助開發者進行單元測試,具有以下特點和功能:
- 創建模擬對象:能通過簡潔的語法創建類或接口的模擬版本,這些模擬對象可作為真實對象的替代品,在調試期間使用,幫助隔離外部依賴。比如在測試一個依賴其他服務(如賬戶服務、數據庫訪問服務等)的業務邏輯時,可模擬這些外部服務,避免在測試中涉及真實的復雜操作(如真的修改數據庫數據、調用遠程接口等) 。
- 定義行為:可以對模擬對象的方法進行 “樁(Stubbing)” 設置,即指定其返回值或讓其拋出異常。例如,使用
when(...).thenReturn(...)
來定義方法返回特定值;用thenThrow()
模擬方法拋出異常,方便測試錯誤處理邏輯。 - 驗證交互:提供驗證機制,通過
verify(...)
方法檢查模擬對象的方法是否按照預期被調用,以及調用的次數,從而驗證代碼與模擬對象之間的交互是否正確
引入的依賴
mockito-core
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-core -->
<dependency><groupId>org.mockito</groupId><artifactId>mockito-core</artifactId><version>5.15.2</version><scope>test</scope>
</dependency>
testng?
<!-- https://mvnrepository.com/artifact/org.testng/testng --><dependency><groupId>org.testng</groupId><artifactId>testng</artifactId><version>7.11.0</version><scope>test</scope></dependency>
為什么需要mock
可以知道這個方法的調用情況,調用了多少次參數就是多少
給這個對象的行為做一個定義,來指定返回結果或者指定特定的動作
當使用mock對象時,如果不對其行為進行定義,則mock對象方法的返回值為返回類型的默認值
驗證和斷言
驗證:vertify()方法
驗證是校驗待驗證的對象是否發生過某些行為
package com.example.testng;import org.mockito.Mockito;
import org.testng.annotations.Test;import java.util.Random;public class MockitoDemoTest {@Testpublic void test(){//mock一個對象Random mock= Mockito.mock(Random.class);//輸出隨機數System.out.println(mock.nextInt());//驗證我們的mock對象是否用了nextInt()這個方法Mockito.verify(mock).nextInt();}
}
為什么我們的執行結果一直是0?
這是因為我們沒有給我們的mock出來的對象定義行為
斷言:Assert
判斷我們返回的結果是否符合我們的預期
package com.example.testng;import org.mockito.Mockito;
import org.testng.Assert;
import org.testng.annotations.Test;import java.util.Random;public class MockitoDemoTest {@Testpublic void test() {//mock一個對象Random mock = Mockito.mock(Random.class);Mockito.when(mock.nextInt()).thenReturn(100);Assert.assertEquals(100, mock.nextInt());}
}
符合預期,我們成功通過
不符合預期,我們測試失敗
package com.example.testng;import org.mockito.Mockito;
import org.testng.Assert;
import org.testng.annotations.Test;import java.util.Random;public class MockitoDemoTest {@Testpublic void test() {//mock一個對象Random mock = Mockito.mock(Random.class);Mockito.when(mock.nextInt()).thenReturn(100);Assert.assertEquals(101, mock.nextInt());}
}
給Mock對象進行打樁
打樁的意思其實就是對mock對象的行為進行定義
打樁的話是mockito里面的when()方法,定義我們的行為
@Mock注解
我們的mock注解必須搭配MockitoAnnotations.openMocks(testClass)方法一起使用
我們使用@Mock注解來Mock對象,而不是在代碼里面手動Mock我們的對象
package com.example.testng;import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.testng.Assert;
import org.testng.annotations.Test;import java.util.Random;public class MockitoDemoTest {@Mockprivate Random mock;@Testpublic void test() {MockitoAnnotations.openMocks(this);Mockito.when(mock.nextInt()).thenReturn(100);Assert.assertEquals(100, mock.nextInt());}
}
@BeforeTets與@AfterTest
package com.example.testng;import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.testng.Assert;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;import java.util.Random;public class MockitoDemoTest {@Mockprivate Random mock;@BeforeTestvoid setup() {System.out.println("測試前的準備");}@AfterTestvoid end() {System.out.println("測試結束");}@Testpublic void test() {MockitoAnnotations.openMocks(this);Mockito.when(mock.nextInt()).thenReturn(100);Assert.assertEquals(100, mock.nextInt());}
}
Spy方法與@Spy注解
spy方法與mock方法的不同是
1.被spy的對象會走真實的方法而mock對象不會
2.spy{}方法的參數是對象實例,mock的參數是class
package com.example.testng;import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import org.testng.Assert;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;import java.util.Random;public class MockitoDemoTest {@Spyprivate Random mock;@BeforeTestvoid setup() {MockitoAnnotations.openMocks(this);}@Testpublic void test() {Assert.assertEquals(100, mock.nextInt());}
}
我們沒有打樁,而且我們是Spy注解,所以我們不會輸出0,而是會去調用真正的Random方法
如果我們打樁了的話,那還是會按照我們打樁定義的規則來
打樁與Mock靜態方法
4個常見的打樁方法
thenCallRealMethod
thenReturn
thenThrow
thenAnswer
package com.example.testng;import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.testng.Assert;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;import java.util.Random;import static org.mockito.Mockito.when;public class MockitoDemoTest {@Spyprivate Random mock;@BeforeTestvoid setup() {MockitoAnnotations.openMocks(this);}@Testpublic void test() {//調用真實方法when(mock.nextInt()).thenCallRealMethod();//定義返回值when(mock.nextInt()).thenReturn(1);//定義返回什么錯誤when(mock.nextInt()).thenThrow(new RuntimeException());//自定義響應邏輯when(mock.nextInt()).thenAnswer(new Answer<Integer>() {@Overridepublic Integer answer(InvocationOnMock invocation) throws Throwable {// 這里可以編寫自定義的邏輯// invocation 包含了方法調用的信息,例如方法名、參數等// 這里簡單返回一個固定值 42return 42;}});}
}
Mock靜態方法
依賴
mockito-inline依賴包含了mockito-code依賴
注意我們使用mockito-inline依賴的時候要把mockito-code依賴注釋掉,我們不能同時引用
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-inline -->
<dependency><groupId>org.mockito</groupId><artifactId>mockito-inline</artifactId><version>5.2.0</version><scope>test</scope>
</dependency>
在軟件開發的單元測試中,有時候我們需要對靜態方法進行模擬(Mock),這樣可以更好地控制測試環境、隔離依賴,提高測試的可維護性和穩定性。下面詳細介紹模擬靜態方法的相關內容,以 Java 語言結合 Mockito 框架為例
為什么需要 Mock 靜態方法
?
隔離依賴:靜態方法可能依賴于外部資源,如文件系統、數據庫等。在單元測試中,我們希望避免這些外部依賴的影響,通過模擬靜態方法可以將測試與外部資源隔離開來
控制返回值:靜態方法的返回值可能受到多種因素的影響,通過模擬靜態方法,我們可以精確控制其返回值,從而更方便地驗證業務邏輯
使用例子
我們這個類里面有個靜態方法
public class StaticUtils {public static int add(int a, int b) {return a + b;}
}
使用mockStatic
使用 try-with-resources語句確保 MockedStatic
對象在使用完畢后自動關閉,避免對后續測試產生影響
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mockStatic;public class StaticUtilsTest {@Testpublic void testMockStaticMethod() {// 使用 try-with-resources 語句創建 MockedStatic 對象try (MockedStatic<StaticUtils> mockedStatic = mockStatic(StaticUtils.class)) {// 定義靜態方法的行為mockedStatic.when(() -> StaticUtils.add(2, 3)).thenReturn(10);// 調用靜態方法int result = StaticUtils.add(2, 3);// 驗證結果assertEquals(10, result);}}
}
單元測試如何提高我們的代碼覆蓋率
@InjectMocks注解
剩下的@mock和@spy注解修飾的對象會自動注入到被InjectMocks注解修飾的對象里面