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

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

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

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

1. 添加 Maven 依賴(lài)

<dependencies>
<!-- Spring Boot 測(cè)試核心依賴(lài) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Mockito 核心庫(kù) -->
<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 測(cè)試) -->
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path-assert</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

二、核心概念與工具

1.JUnit 5 注解

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

2.Mockito 注解

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

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

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

1. 被測(cè)試類(lèi)

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. 單元測(cè)試

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("測(cè)試獲取用戶(hù) - 用戶(hù)存在")
void testGetUserById_UserExists() {
// 1. 模擬 Repository 行為
User mockUser = new User(1L, "Alice", "alice@example.com");
when(userRepository.findById(1L)).thenReturn(Optional.of(mockUser));

// 2. 執(zhí)行測(cè)試方法
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("測(cè)試獲取用戶(hù) - 用戶(hù)不存在")
void testGetUserById_UserNotExists() {
// 1. 模擬 Repository 行為
when(userRepository.findById(999L)).thenReturn(Optional.empty());

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

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

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

1. 被測(cè)試類(lèi)

@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. 單元測(cè)試

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("測(cè)試獲取用戶(hù) - 用戶(hù)存在")
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 請(qǐng)求
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("測(cè)試獲取用戶(hù) - 用戶(hù)不存在")
void testGetUserById_UserNotExists() throws Exception {
// 1. 模擬 Service 返回值
when(userService.getUserById(999L)).thenReturn(null);

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

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

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

@DataJpaTest
public class UserRepositoryTest {

@Autowired
private UserRepository userRepository;

@Test
@DisplayName("測(cè)試保存用戶(hù)")
void testSaveUser() {
// 1. 準(zhǔn)備測(cè)試數(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ù)庫(kù)記錄
User foundUser = userRepository.findById(savedUser.getId()).orElse(null);
assertNotNull(foundUser);
assertEquals("Bob", foundUser.getName());
}

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

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

案例 4:Service 層測(cè)試(結(jié)合異常場(chǎng)景)

1. 被測(cè)試類(lèi)

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

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

2. 單元測(cè)試

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("測(cè)試獲取用戶(hù) - 拋出異常")
void testGetUserById_ThrowsException() {
// 1. 模擬 Repository 行為
when(userRepository.findById(999L)).thenReturn(Optional.empty());

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

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

四、Mockito 高級(jí)用法

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)格測(cè)試

@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());
}

五、測(cè)試覆蓋率與報(bào)告

1.Jacoco 覆蓋率報(bào)告

1.1 添加依賴(lài)

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

1.2 執(zhí)行測(cè)試并生成報(bào)告

mvn clean test
mvn jacoco:report

1.3 查看報(bào)告

報(bào)告路徑:target/site/jacoco/index.html

六、常見(jiàn)問(wèn)題與解決方案

1.問(wèn)題:@Mock注解未生效

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

2.問(wèn)題:Spring 上下文未加載

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

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

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

4.問(wèn)題:測(cè)試數(shù)據(jù)庫(kù)污染

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

七、總結(jié)

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

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

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

測(cè)試原則

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

測(cè)試目標(biāo)

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

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

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

相關(guān)文章

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

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

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

    Java?枚舉的常用技巧匯總

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

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

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

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

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

    史上最全Java8日期時(shí)間工具類(lèi)(分享)

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

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

    這篇文章主要介紹了詳解Spring Boot自動(dòng)裝配Ribbon哪些核心操作的哪些操作,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(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ì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    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ù)會(huì)繼續(xù)新增),需要的朋友可以參考下
    2023-09-09
  • 分布式醫(yī)療掛號(hào)系統(tǒng)SpringCache與Redis為數(shù)據(jù)字典添加緩存

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

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

最新評(píng)論

千阳县| 襄垣县| 公安县| 宁化县| 峡江县| 邯郸县| 娄烦县| 策勒县| 佛坪县| 曲水县| 宁安市| 乌什县| 广丰县| 瓦房店市| 阳泉市| 公安县| 甘德县| 巢湖市| 黑水县| 长白| 乌拉特中旗| 社旗县| 习水县| 大关县| 西藏| 微博| 济源市| 梁平县| 合作市| 承德市| 绥德县| 九龙县| 韶山市| 庄河市| 汾西县| 永昌县| 崇仁县| 西吉县| 都昌县| 牟定县| 萍乡市|