最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot整合Mockito進(jìn)行單元測試實(shí)踐

 更新時間:2026年05月10日 10:55:18   作者:Lisonseekpan  
本文介紹了Spring Boot結(jié)合JUnit5和Mockito進(jìn)行單元測試的方法,涵蓋環(huán)境準(zhǔn)備、核心概念、實(shí)戰(zhàn)案例、高級用法、覆蓋率工具等內(nèi)容,同時提供了常見問題解決方案和測試原則建議,幫助讀者全面掌握測試技巧,編寫高效可靠的測試代碼

一、環(huán)境準(zhǔn)備

1. 添加 Maven 依賴

<dependencies>
<!-- Spring Boot 測試核心依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Mockito 核心庫 -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<!-- 用于斷言的實(shí)用工具 -->
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<!-- JSON 路徑驗(yàn)證(用于 Controller 測試) -->
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path-assert</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

二、核心概念與工具

1.JUnit 5 注解

注解用途
@Test標(biāo)記測試方法
@BeforeEach每個測試方法執(zhí)行前調(diào)用
@AfterEach每個測試方法執(zhí)行后調(diào)用
@DisplayName為測試方法設(shè)置可讀名稱
@ParameterizedTest支持參數(shù)化測試(多組輸入測試同一邏輯)

2.Mockito 注解

注解用途
@Mock創(chuàng)建模擬對象(非 Spring 容器管理)
@InjectMocks自動注入被測試類(配合 @Mock 使用)
@MockBean在 Spring 容器中創(chuàng)建模擬 Bean(適用于 @SpringBootTest)
@Spy創(chuàng)建部分模擬對象(真實(shí)方法調(diào)用 + 指定方法模擬)

三、實(shí)戰(zhàn)案例詳解

案例 1:純 Java 服務(wù)類測試(無 Spring 上下文)

1. 被測試類

public class UserService {
private final UserRepository userRepository;

public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}

public User getUserById(Long id) {
return userRepository.findById(id).orElse(null);
}
}

2. 單元測試

import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.assertj.core.api.Assertions.assertThat;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

public class UserServiceTest {

@Mock
private UserRepository userRepository;

@InjectMocks
private UserService userService;

@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
}

@Test
@DisplayName("測試獲取用戶 - 用戶存在")
void testGetUserById_UserExists() {
// 1. 模擬 Repository 行為
User mockUser = new User(1L, "Alice", "alice@example.com");
when(userRepository.findById(1L)).thenReturn(Optional.of(mockUser));

// 2. 執(zhí)行測試方法
User result = userService.getUserById(1L);

// 3. 驗(yàn)證結(jié)果
assertNotNull(result);
assertEquals("Alice", result.getName());
assertEquals("alice@example.com", result.getEmail());

// 4. 驗(yàn)證方法調(diào)用
verify(userRepository, times(1)).findById(1L);
}

@Test
@DisplayName("測試獲取用戶 - 用戶不存在")
void testGetUserById_UserNotExists() {
// 1. 模擬 Repository 行為
when(userRepository.findById(999L)).thenReturn(Optional.empty());

// 2. 執(zhí)行測試方法
User result = userService.getUserById(999L);

// 3. 驗(yàn)證結(jié)果
assertNull(result);
verify(userRepository, times(1)).findById(999L);
}
}

案例 2:Spring Boot Controller 層測試(使用 MockMvc)

1. 被測試類

@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserService userService;

@GetMapping("/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
User user = userService.getUserById(id);
return ResponseEntity.ok(user);
}
}

2. 單元測試

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.web.servlet.MockMvc;

@WebMvcTest(UserController.class)
public class UserControllerTest {

@Autowired
private MockMvc mockMvc;

@MockBean
private UserService userService;

@Test
@DisplayName("測試獲取用戶 - 用戶存在")
void testGetUserById_UserExists() throws Exception {
// 1. 模擬 Service 返回值
User mockUser = new User(1L, "Alice", "alice@example.com");
when(userService.getUserById(1L)).thenReturn(mockUser);

// 2. 發(fā)起 HTTP 請求
mockMvc.perform(get("/users/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(1L))
.andExpect(jsonPath("$.name").value("Alice"))
.andExpect(jsonPath("$.email").value("alice@example.com"))
.andDo(print());
}

@Test
@DisplayName("測試獲取用戶 - 用戶不存在")
void testGetUserById_UserNotExists() throws Exception {
// 1. 模擬 Service 返回值
when(userService.getUserById(999L)).thenReturn(null);

// 2. 發(fā)起 HTTP 請求
mockMvc.perform(get("/users/999"))
.andExpect(status().isNotFound());
}
}

案例 3:Repository 層測試(使用內(nèi)存數(shù)據(jù)庫 H2)

1. 配置 H2 內(nèi)存數(shù)據(jù)庫

@DataJpaTest
public class UserRepositoryTest {

@Autowired
private UserRepository userRepository;

@Test
@DisplayName("測試保存用戶")
void testSaveUser() {
// 1. 準(zhǔn)備測試數(shù)據(jù)
User user = new User(null, "Bob", "bob@example.com");
User savedUser = userRepository.save(user);

// 2. 驗(yàn)證結(jié)果
assertNotNull(savedUser.getId());
assertEquals("Bob", savedUser.getName());
assertEquals("bob@example.com", savedUser.getEmail());

// 3. 驗(yàn)證數(shù)據(jù)庫記錄
User foundUser = userRepository.findById(savedUser.getId()).orElse(null);
assertNotNull(foundUser);
assertEquals("Bob", foundUser.getName());
}

@Test
@DisplayName("測試查詢用戶")
void testFindByEmail() {
// 1. 準(zhǔn)備測試數(shù)據(jù)
User user = new User(null, "Charlie", "charlie@example.com");
userRepository.save(user);

// 2. 查詢驗(yàn)證
User result = userRepository.findByEmail("charlie@example.com");
assertNotNull(result);
assertEquals("Charlie", result.getName());
assertEquals("charlie@example.com", result.getEmail());
}
}

案例 4:Service 層測試(結(jié)合異常場景)

1. 被測試類

@Service
public class UserService {
@Autowired
private UserRepository userRepository;

public User getUserById(Long id) {
return userRepository.findById(id).orElseThrow(() -> new UserNotFoundException("User not found"));
}
}

2. 單元測試

import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;

public class UserServiceTest {

@Mock
private UserRepository userRepository;

@InjectMocks
private UserService userService;

@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
}

@Test
@DisplayName("測試獲取用戶 - 拋出異常")
void testGetUserById_ThrowsException() {
// 1. 模擬 Repository 行為
when(userRepository.findById(999L)).thenReturn(Optional.empty());

// 2. 執(zhí)行測試并捕獲異常
assertThrows(UserNotFoundException.class, () -> {
userService.getUserById(999L);
});

// 3. 驗(yàn)證方法調(diào)用
verify(userRepository, times(1)).findById(999L);
}
}

四、Mockito 高級用法

1.模擬任意參數(shù)

when(userService.getUserById(anyLong())).thenReturn(new User(1L, "Test User"));

2.參數(shù)捕獲與驗(yàn)證

ArgumentCaptor<User> userCaptor = ArgumentCaptor.forClass(User.class);
verify(userRepository).save(userCaptor.capture());
User capturedUser = userCaptor.getValue();
assertEquals("Alice", capturedUser.getName());

3.部分模擬(Spy)

@Spy
private UserService userService;

@Test
void testSpy() {
doReturn(new User(1L, "Mock User")).when(userService).getUserById(anyLong());
User result = userService.getUserById(1L);
assertEquals("Mock User", result.getName());
}

4.BDD 風(fēng)格測試

@Test
void testBddStyle() {
// Given
User mockUser = new User(1L, "Alice");
when(userService.getUserById(1L)).thenReturn(mockUser);

// When
User result = userService.getUserById(1L);

// Then
assertNotNull(result);
assertEquals("Alice", result.getName());
}

五、測試覆蓋率與報告

1.Jacoco 覆蓋率報告

1.1 添加依賴

<dependency>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.11</version>
</dependency>

1.2 執(zhí)行測試并生成報告

mvn clean test
mvn jacoco:report

1.3 查看報告

報告路徑:target/site/jacoco/index.html

六、常見問題與解決方案

1.問題:@Mock注解未生效

  • 原因:未調(diào)用 MockitoAnnotations.openMocks(this)
  • 解決方案:在 @BeforeEach 方法中初始化

2.問題:Spring 上下文未加載

  • 原因:未使用 @SpringBootTest
  • 解決方案:使用 @SpringBootTest 或更細(xì)粒度的測試注解(如 @WebMvcTest

3.問題:測試方法執(zhí)行順序異常

  • 原因:JUnit 5 默認(rèn)按字母順序執(zhí)行測試方法
  • 解決方案:使用 @Order 注解或 @TestMethodOrder

4.問題:測試數(shù)據(jù)庫污染

  • 原因:測試數(shù)據(jù)未清理
  • 解決方案:使用 @DirtiesContext 或手動清理數(shù)據(jù)

七、總結(jié)

測試類型推薦注解適用場景
純 Java 服務(wù)類@Mock + @InjectMocks無 Spring 上下文的單元測試
Spring Controller@WebMvcTest + @MockBeanHTTP 接口測試
Repository 層@DataJpaTest數(shù)據(jù)庫操作驗(yàn)證
集成測試@SpringBootTest多組件協(xié)作驗(yàn)證

八、擴(kuò)展學(xué)習(xí)

  1. 測試框架對比:JUnit 5 vs TestNG
  2. 測試覆蓋率工具:Jacoco、Clover
  3. 測試驅(qū)動開發(fā)(TDD):先寫測試再寫代碼
  4. 持續(xù)集成(CI):GitHub Actions + Test Coverage

測試原則

  • 單元測試:關(guān)注單一職責(zé),快速失敗,無需外部依賴
  • 集成測試:驗(yàn)證組件協(xié)作,模擬真實(shí)環(huán)境
  • 測試覆蓋率:追求 80%+ 核心邏輯覆蓋,避免過度測試

測試目標(biāo)

  • 所有邊界條件都被覆蓋
  • 所有異常場景都被模擬
  • 所有業(yè)務(wù)邏輯都有驗(yàn)證

通過本教程,您應(yīng)能全面掌握 Spring Boot 與 Mockito 的單元測試技巧,并能夠編寫高效、可靠的測試代碼。

以上為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java導(dǎo)致內(nèi)存泄漏的多種情況分析

    Java導(dǎo)致內(nèi)存泄漏的多種情況分析

    本文介紹了Java中常見的內(nèi)存泄漏情況,包括生命周期長的集合、未關(guān)閉的資源連接、ThreadLocal使用不當(dāng)、內(nèi)部類與外部類引用非靜態(tài)內(nèi)部類、監(jiān)聽器與回調(diào)注冊后沒有注銷,推薦使用MAT和VisualVM等工具進(jìn)行內(nèi)存泄漏排查,感興趣的朋友跟隨小編一起看看吧
    2026-01-01
  • Java?枚舉的常用技巧匯總

    Java?枚舉的常用技巧匯總

    在Java中,枚舉類型是一種特殊的數(shù)據(jù)類型,允許定義一組固定的常量,默認(rèn)情況下,toString方法返回枚舉常量的名稱,本文提供了一個完整的代碼示例,展示了如何在Java中通過重寫枚舉的toString方法來展示枚舉實(shí)例的字段信息,感興趣的朋友一起看看吧
    2025-01-01
  • Java 高并發(fā)二:多線程基礎(chǔ)詳細(xì)介紹

    Java 高并發(fā)二:多線程基礎(chǔ)詳細(xì)介紹

    本文主要介紹Java 高并發(fā)多線程的知識,這里整理詳細(xì)的資料來解釋線程的知識,有需要的學(xué)習(xí)高并發(fā)的朋友可以參考下
    2016-09-09
  • 詳解如何實(shí)現(xiàn)nacos的配置的熱更新

    詳解如何實(shí)現(xiàn)nacos的配置的熱更新

    這篇文章主要為大家詳細(xì)介紹了如何實(shí)現(xiàn)nacos的配置的熱更新,文中的示例代碼講解詳細(xì),具有一定的借鑒價值,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-12-12
  • 史上最全Java8日期時間工具類(分享)

    史上最全Java8日期時間工具類(分享)

    這篇文章主要介紹了史上最全Java8日期時間工具類(分享),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • 手把手帶你分析SpringBoot自動裝配完成了Ribbon哪些核心操作

    手把手帶你分析SpringBoot自動裝配完成了Ribbon哪些核心操作

    這篇文章主要介紹了詳解Spring Boot自動裝配Ribbon哪些核心操作的哪些操作,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-08-08
  • Java中的異常處理機(jī)制try-catch詳解

    Java中的異常處理機(jī)制try-catch詳解

    這篇文章主要介紹了Java中的異常處理機(jī)制try-catch詳解,本文給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧
    2024-12-12
  • 基于SpringBoot實(shí)現(xiàn)圖片上傳與顯示

    基于SpringBoot實(shí)現(xiàn)圖片上傳與顯示

    這篇文章主要為大家詳細(xì)介紹了基于SpringBoot實(shí)現(xiàn)圖片上傳與顯示,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-08-08
  • SpringBoot實(shí)現(xiàn)HTTP調(diào)用的七種方式總結(jié)

    SpringBoot實(shí)現(xiàn)HTTP調(diào)用的七種方式總結(jié)

    小編在工作中,遇到一些需要調(diào)用三方接口的任務(wù),就需要用到 HTTP 調(diào)用工具,這里,我總結(jié)了一下 實(shí)現(xiàn) HTTP 調(diào)用的方式,共有 7 種(后續(xù)會繼續(xù)新增),需要的朋友可以參考下
    2023-09-09
  • 分布式醫(yī)療掛號系統(tǒng)SpringCache與Redis為數(shù)據(jù)字典添加緩存

    分布式醫(yī)療掛號系統(tǒng)SpringCache與Redis為數(shù)據(jù)字典添加緩存

    這篇文章主要為大家介紹了分布式醫(yī)療掛號系統(tǒng)SpringCache與Redis為數(shù)據(jù)字典添加緩存,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-04-04

最新評論

巴东县| 古田县| 红安县| 县级市| 景德镇市| 安宁市| 乌拉特后旗| 涿州市| 丹江口市| 醴陵市| 宁乡县| 当阳市| 工布江达县| 利辛县| 营山县| 遵义市| 太保市| 徐水县| 永寿县| 文山县| 鄂温| 巨鹿县| 宣威市| 兖州市| 蕲春县| 灵石县| 阜城县| 巩留县| 旬阳县| 龙南县| 鞍山市| 辽中县| 凭祥市| 花垣县| 铜鼓县| 潞西市| 库车县| 海丰县| 建平县| 平定县| 眉山市|