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

Springboot結(jié)合Junit單元測(cè)試的實(shí)現(xiàn)

 更新時(shí)間:2026年03月23日 10:08:23   作者:Darren245  
Spring Boot框架通過(guò)整合JUnit,為開(kāi)發(fā)者提供了便捷的單元測(cè)試支持,本文就來(lái)介紹Springboot結(jié)合Junit單元測(cè)試的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

一、junit4與junit5及springboot中的使用

在現(xiàn)代軟件開(kāi)發(fā)中,單元測(cè)試是確保代碼質(zhì)量的重要環(huán)節(jié)。Spring Boot框架通過(guò)整合JUnit,為開(kāi)發(fā)者提供了便捷的單元測(cè)試支持。

1.1 Spring Boot中JUnit版本的變化

在Spring Boot 2.0之前,框架默認(rèn)使用JUnit 4作為測(cè)試平臺(tái)。然而,從Spring Boot 2.0開(kāi)始,JUnit 5成為默認(rèn)的測(cè)試框架。以下是Spring Boot不同版本中JUnit版本的對(duì)比:

Spring Boot版本默認(rèn)JUnit版本
1.xJUnit 4
2.xJUnit 5

例如,Spring Boot 2.2.0使用JUnit 5.5.2版本。開(kāi)發(fā)者可以通過(guò)POM文件確認(rèn)具體版本。

1.2 POM文件配置

在Spring Boot項(xiàng)目中,單元測(cè)試的依賴通過(guò)spring-boot-starter-test啟動(dòng)器引入。以下是POM文件的配置示例:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    <exclusions>
        <exclusion>
            <groupId>org.junit.vintage</groupId>
            <artifactId>junit-vintage-engine</artifactId>
        </exclusion>
    </exclusions>
</dependency>

1.3 排除JUnit Vintage Engine

junit-vintage-engine是JUnit 3和JUnit 4的運(yùn)行支持平臺(tái)。默認(rèn)情況下,Spring Boot測(cè)試啟動(dòng)器會(huì)排除該依賴,以鼓勵(lì)開(kāi)發(fā)者使用JUnit 5。如果需要使用JUnit 4,可以移除<exclusions>標(biāo)簽。

二、JUnit 4與JUnit 5的對(duì)比

以下是JUnit 4和JUnit 5的主要差異:

特性JUnit 4JUnit 5
注解@RunWith、@Test@SpringBootTest、@Test
默認(rèn)啟動(dòng)類支持需要手動(dòng)指定啟動(dòng)類自動(dòng)檢測(cè)啟動(dòng)類
測(cè)試方法支持需要@Test注解需要@Test注解
擴(kuò)展支持有限更強(qiáng)大的擴(kuò)展機(jī)制

三、JUnit 4測(cè)試代碼示例

以下是一個(gè)基于JUnit 4的測(cè)試代碼示例:

import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.junit.Test;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = SpringBootExceptionAndJourneyApplication.class)
public class UserServiceTest {
    @Test
    public void testAddUser() {
        System.out.println("JUnit 4 測(cè)試方法運(yùn)行成功!");
    }
}

四、JUnit 5測(cè)試代碼示例

以下是一個(gè)基于JUnit 5的測(cè)試代碼示例:

import org.springframework.boot.test.context.SpringBootTest;
import org.junit.jupiter.api.Test;

@SpringBootTest
public class UserServiceTest {

    @Test
    public void testAddUser() {
        System.out.println("JUnit 5 測(cè)試方法運(yùn)行成功!");
    }
}

五、實(shí)際案例:持久層與業(yè)務(wù)層測(cè)試

假設(shè)我們有一個(gè)UserDAO和UserService,以下是它們的實(shí)現(xiàn)代碼:

5.1 持久層代碼

public class UserDAOImpl {
    public void insert() {
        System.out.println("INSERT INTO USER VALUES(...)");
    }
}

5.2 業(yè)務(wù)層代碼

import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl {
    private final UserDAOImpl userDAO;
    public UserServiceImpl(UserDAOImpl userDAO) {
        this.userDAO = userDAO;
    }
    public void addUser() {
        userDAO.insert();
    }
}

5.3 測(cè)試代碼

import org.springframework.boot.test.context.SpringBootTest;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
@SpringBootTest
public class UserServiceTest {
    @Autowired
    private UserServiceImpl userService;
    @Test
    public void testAddUser() {
        userService.addUser();
    }
}

六、常見(jiàn)問(wèn)題與解答

以下是關(guān)于Spring Boot整合JUnit單元測(cè)試的常見(jiàn)問(wèn)題:

問(wèn)題答案
1. Spring Boot默認(rèn)使用哪個(gè)JUnit版本?Spring Boot 2.x默認(rèn)使用JUnit 5。
2. 如何在Spring Boot中使用JUnit 4?需要移除POM文件中junit-vintage-engine的排除配置。
3. JUnit 5是否需要指定啟動(dòng)類?默認(rèn)情況下,JUnit 5會(huì)自動(dòng)檢測(cè)啟動(dòng)類。如果項(xiàng)目中有多個(gè)啟動(dòng)類,需要通過(guò)@SpringBootTest(classes = ...)指定。
4. JUnit 5的測(cè)試方法是否需要@RunWith注解?不需要,JUnit 5使用@SpringBootTest注解即可。
5. 如何確認(rèn)JUnit版本?通過(guò)POM文件中的依賴版本確認(rèn),例如junit-jupiter-api的版本號(hào)。

七、流程圖:Spring Boot整合JUnit單元測(cè)試的步驟

創(chuàng)建Spring Boot項(xiàng)目添加spring-boot-starter-test依賴確認(rèn)JUnit版本編寫(xiě)測(cè)試代碼運(yùn)行測(cè)試

第一部分總結(jié)

我們現(xiàn)在直接使用junit5在springboot項(xiàng)目中使用即可。使用方式為添加注解@SpringBootTest(會(huì)自己找啟動(dòng)類,有多個(gè)則通過(guò)classes = ...指定)就可以讓容器啟動(dòng)來(lái)測(cè)試。

疑問(wèn)1:不是說(shuō)5的話boot項(xiàng)目需要ext注解嗎?

AI解答: @ExtendWith 確實(shí)是 JUnit 5 的核心注解。

不過(guò),在 Spring Boot 項(xiàng)目里,你通常不需要手動(dòng)添加它

?? 為什么會(huì)有這個(gè)誤解?

你的理解是對(duì)的:JUnit 5 確實(shí)使用 @ExtendWith 來(lái)集成 Spring 的功能,它取代了 JUnit 4 的 @RunWith。

在早期版本的 Spring Boot (2.1.x 之前),確實(shí)需要手動(dòng)在測(cè)試類上寫(xiě)上 @ExtendWith(SpringExtension.class)。

? 現(xiàn)在的實(shí)際情況是怎樣的?

從 Spring Boot 2.1.x 版本開(kāi)始,官方為了讓開(kāi)發(fā)更便捷,做了一個(gè)“貼心”的優(yōu)化。

@SpringBootTest 這個(gè)我們最常用的注解,其內(nèi)部源碼已經(jīng)自動(dòng)包含了 @ExtendWith(SpringExtension.class)。

你可以把它理解為:@SpringBootTest 是一個(gè)“大禮包”,里面已經(jīng)幫你把 @ExtendWith 這個(gè)“工具”放進(jìn)去了。所以,當(dāng)你使用 @SpringBootTest 時(shí),就無(wú)需再重復(fù)添加了。

代碼對(duì)比
  • 早期寫(xiě)法 (已過(guò)時(shí))

    // 在 Spring Boot 2.1 之前,需要這樣寫(xiě)
    @ExtendWith(SpringExtension.class) // 手動(dòng)啟用 Spring 擴(kuò)展
    @SpringBootTest
    public class OrderServiceTest {
        // ...
    }
    
  • 現(xiàn)在寫(xiě)法 (推薦) // 現(xiàn)在,這樣寫(xiě)就足夠了! @SpringBootTest public class OrderServiceTest { // ... }

?? 什么時(shí)候才需要手動(dòng)寫(xiě)@ExtendWith?

只有在一些不使用 @SpringBootTest 的底層測(cè)試場(chǎng)景中,你才可能需要手動(dòng)添加。

例如,如果你只用 @ContextConfiguration 來(lái)加載一個(gè)特定的 Spring 配置文件進(jìn)行測(cè)試,這時(shí)就需要手動(dòng)通過(guò) @ExtendWith(SpringExtension.class) 來(lái)啟用 Spring 的擴(kuò)展支持。

但對(duì)于絕大多數(shù) Spring Boot 的集成測(cè)試來(lái)說(shuō),放心地只使用 @SpringBootTest 吧!

疑問(wèn)2:有必要每次啟動(dòng)整個(gè)容器嗎?請(qǐng)看第二部分

第二部分 springboot項(xiàng)目怎么進(jìn)行mock

很多開(kāi)發(fā)者在剛開(kāi)始寫(xiě)測(cè)試時(shí),都會(huì)糾結(jié)是“啟動(dòng)整個(gè) Spring 容器”還是“只測(cè)一個(gè)類”。

簡(jiǎn)單來(lái)說(shuō), “優(yōu)雅”的測(cè)試 = 選對(duì)測(cè)試類型 + 用好 Mock 隔離

?? 一、Mock 是啥?解決啥問(wèn)題?

1. 通俗解釋:什么是 Mock?

想象一下,你是一名汽車(chē)引擎設(shè)計(jì)師(你在測(cè)試 UserService),你需要測(cè)試引擎(UserService)的運(yùn)轉(zhuǎn)是否良好。

  • 如果不使用 Mock:你必須把引擎裝到整輛車(chē)?yán)?,連上油箱、排氣管、輪胎,甚至要把車(chē)開(kāi)到路上(啟動(dòng) Spring 容器、連接真實(shí)數(shù)據(jù)庫(kù)、連接真實(shí) Redis)。這非常慢,而且如果車(chē)打不著火,你不知道是引擎壞了,還是油箱漏了,還是輪胎沒(méi)氣。

  • 使用 Mock:你在實(shí)驗(yàn)室里,給引擎接上一個(gè)模擬油箱(Mock Repository)和一個(gè)模擬排氣管(Mock EmailService)。

    • 你可以控制“模擬油箱”里有多少油(Stubbing:預(yù)設(shè)返回值)。
    • 你可以觀察引擎是否真的向“模擬排氣管”排氣了(Verification:驗(yàn)證調(diào)用)。
    • 重點(diǎn):你只測(cè)試引擎本身,不關(guān)心外面的世界。

2. Mock 解決了什么問(wèn)題?

  • 速度極快:不需要啟動(dòng) Spring 容器,不需要連接數(shù)據(jù)庫(kù)(IO 操作最耗時(shí))。單元測(cè)試通常是毫秒級(jí)的。
  • 隔離性強(qiáng):如果測(cè)試失敗了,肯定是你的 Service 邏輯寫(xiě)錯(cuò)了,而不是因?yàn)閿?shù)據(jù)庫(kù)連不上,或者網(wǎng)絡(luò)波動(dòng)。
  • 覆蓋極端情況:你可以輕松模擬“數(shù)據(jù)庫(kù)掛了”或者“查不到數(shù)據(jù)”的場(chǎng)景,而在真實(shí)環(huán)境中很難故意制造這些故障。

? 二、如何“優(yōu)雅”地執(zhí)行 Boot 項(xiàng)目單元測(cè)試?

在 Spring Boot 中,優(yōu)雅的核心在于 “各司其職” 。不要把所有測(cè)試都寫(xiě)成 @SpringBootTest(啟動(dòng)全容器),那樣太慢了。

我們需要區(qū)分兩種測(cè)試策略:

1. 純單元測(cè)試 (Unit Test) —— 推薦用于 Service 層

特點(diǎn):完全不啟動(dòng) Spring 容器,純 Java 代碼運(yùn)行。
工具:JUnit 5 + Mockito (@Mock@InjectMocks)。

場(chǎng)景:測(cè)試 UserService 里的業(yè)務(wù)邏輯(比如計(jì)算價(jià)格、校驗(yàn)參數(shù))。

代碼示例

// 1. 不需要 @SpringBootTest,不需要啟動(dòng)容器!
// 使用 Mockito 的擴(kuò)展來(lái)初始化 Mock 對(duì)象
@ExtendWith(MockitoExtension.class) 
class UserServiceTest {
    // 2. @Mock: 創(chuàng)建一個(gè)假的 UserRepository,它是空的,需要你喂數(shù)據(jù)
    @Mock
    private UserRepository userRepository;
    // 3. @InjectMocks: 創(chuàng)建 UserService 實(shí)例,并把上面的 userRepository 塞進(jìn)去
    @InjectMocks
    private UserService userService;
    @Test
    void shouldFindUserById() {
        // --- Arrange (準(zhǔn)備) ---
        User mockUser = new User(1L, "Alice");
        // 告訴 Mock 對(duì)象:當(dāng)有人調(diào)用 findById(1L) 時(shí),返回 mockUser
        when(userRepository.findById(1L)).thenReturn(Optional.of(mockUser));
        // --- Act (執(zhí)行) ---
        User result = userService.findById(1L);
        // --- Assert (斷言) ---
        assertThat(result.getName()).isEqualTo("Alice");
        // --- Verify (驗(yàn)證) ---
        // 驗(yàn)證 userRepository.findById 是否真的被調(diào)用了一次
        verify(userRepository, times(1)).findById(1L);
    }
}

優(yōu)雅點(diǎn):速度飛快,完全隔離。

2. 切片測(cè)試 / 集成測(cè)試 (Slice Test) —— 推薦用于 Controller 或 Repository

特點(diǎn):只啟動(dòng) Spring 容器的一部分(比如只啟動(dòng) Web 層,或者只啟動(dòng) JPA 層)。
工具@WebMvcTest (控制器), @DataJpaTest (數(shù)據(jù)庫(kù)), @MockBean。

場(chǎng)景:測(cè)試 UserController 的接口映射是否正確,或者測(cè)試 SQL 語(yǔ)句是否正確。

代碼示例 (測(cè)試 Controller)

// 1. @WebMvcTest: 只啟動(dòng) Web 層相關(guān)的 Bean (Controller, Converter 等),不啟動(dòng) Service
@WebMvcTest(UserController.class) 
class UserControllerTest {
    @Autowired
    private MockMvc mockMvc; // Spring 提供的模擬 HTTP 客戶端
    // 2. @MockBean: 這是 Spring 的注解!
    // 它會(huì)去 Spring 容器里,把 UserService 替換成一個(gè) Mock 對(duì)象
    @MockBean
    private UserService userService;
    @Test
    void shouldReturnUserJson() throws Exception {
        // --- Arrange ---
        // 模擬 Service 層返回?cái)?shù)據(jù)
        when(userService.findById(1L)).thenReturn(new User(1L, "Alice"));
        // --- Act & Assert ---
        // 發(fā)送一個(gè)模擬的 GET 請(qǐng)求
        mockMvc.perform(get("/users/1"))
               .andExpect(status().isOk()) // 期望狀態(tài)碼 200
               .andExpect(jsonPath("$.name").value("Alice")); // 期望返回 JSON 中有 name: Alice
    }
}

優(yōu)雅點(diǎn):比 @SpringBootTest 快,但又能測(cè)試 Spring 的注解(如 @RestController@RequestMapping)是否生效。

?? 三、總結(jié):Mock 注解對(duì)比表

這是最容易混淆的地方,請(qǐng)注意區(qū)分:

表格

特性@Mock(Mockito)@MockBean(Spring Boot)
所屬庫(kù)MockitoSpring Boot Test
是否啟動(dòng) Spring (純單元測(cè)試) (集成測(cè)試/切片測(cè)試)
作用范圍僅在測(cè)試類內(nèi)部有效會(huì)替換 Spring 容器中的 Bean
使用場(chǎng)景測(cè)試 Service 業(yè)務(wù)邏輯測(cè)試 Controller, 或者需要 Spring 注入的場(chǎng)景
性能極快 (毫秒級(jí))較快 (秒級(jí),取決于加載多少組件)

?? 四、最佳實(shí)踐建議

  • Service 層:優(yōu)先使用  @ExtendWith(MockitoExtension.class) + @Mock。不要?jiǎng)硬粍?dòng)就 @SpringBootTest,那樣太慢了。
  • Controller 層:使用  @WebMvcTest + @MockBean。
  • Repository 層:使用  @DataJpaTest (它會(huì)自動(dòng)配置內(nèi)存數(shù)據(jù)庫(kù) H2)。
  • 全鏈路測(cè)試:只有當(dāng)你需要測(cè)試“整個(gè)應(yīng)用能不能跑起來(lái)”或者“配置類是否正確”時(shí),才使用  @SpringBootTest。

這樣分層測(cè)試,你的項(xiàng)目構(gòu)建速度會(huì)非常快,而且邏輯清晰,維護(hù)起來(lái)也很優(yōu)雅。

第二部分總結(jié)

使用mock可以最小化范圍測(cè)試,而不是啟動(dòng)整個(gè)容器。一般測(cè)試的都是service層,直接使用@ExtendWith(MockitoExtension.class) + @Mock。不要?jiǎng)硬粍?dòng)就 @SpringBootTest,那樣太慢了

第三部分 Mock中的常見(jiàn)問(wèn)題

一、mock原理

就是 “偽造” 依賴接口 / 對(duì)象 / 函數(shù)的返回結(jié)果,讓程序在沒(méi)有真實(shí)后端、真實(shí)服務(wù)時(shí)也能正常跑、正常測(cè)。原理為通過(guò)動(dòng)態(tài)代理、字節(jié)碼增強(qiáng)或請(qǐng)求攔截等方式,劫持目標(biāo)方法 / 接口調(diào)用,跳過(guò)真實(shí)邏輯執(zhí)行并直接返回預(yù)設(shè)偽造數(shù)據(jù),從而實(shí)現(xiàn)依賴隔離與行為模擬。

? 疑問(wèn):final類怎么模擬呢?

在 Java 中,final 關(guān)鍵字的設(shè)計(jì)初衷就是為了防止繼承(類)或重寫(xiě)(方法)。而 Mockito 的核心原理恰恰是生成子類(動(dòng)態(tài)代理)來(lái)攔截方法調(diào)用。 所以,默認(rèn)情況下,Mockito 無(wú)法 Mock final 類或 final 方法。如果你強(qiáng)行去 mock,通常會(huì)報(bào) Cannot mock/spy class ... final class 的錯(cuò)誤。

?? 方案:使用mockito-inline(推薦,現(xiàn)代做法)

這是目前最主流的做法。從 Mockito 2.x 后期版本開(kāi)始,官方提供了一個(gè)擴(kuò)展模塊 mockito-inline,它利用 Java Instrumentation API 在運(yùn)行時(shí)修改字節(jié)碼,從而支持 Mock final 類。

適用場(chǎng)景:Spring Boot 2.x (較新版本) 或 Spring Boot 3.x,且你不想引入沉重的 PowerMock。

1. 添加依賴

雖然 Spring Boot 的 spring-boot-starter-test 已經(jīng)包含了 mockito-core,但你需要額外引入 mockito-inline。

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-inline</artifactId>
    <version>5.x.x</version> <!-- 版本號(hào)通常與 mockito-core 保持一致 -->
    <scope>test</scope>
</dependency>

2. 開(kāi)啟配置(關(guān)鍵步驟)

僅僅加依賴是不夠的,你必須告訴 Mockito 使用這個(gè)“內(nèi)聯(lián)”模式。
在 src/test/resources 目錄下創(chuàng)建一個(gè)文件夾 mockito-extensions,并在其中創(chuàng)建一個(gè)文件 org.mockito.plugins.MockMaker。

  • 文件路徑:src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker

  • 文件內(nèi)容:

    mock-maker=inline

3. 編寫(xiě)測(cè)試

配置好后,你就可以像 Mock 普通類一樣 Mock final 類了,代碼完全不用變:

// 假設(shè) FinalService 是一個(gè) final 類
final class FinalService {
    public String sayHello() { return "Hello"; }
}
@ExtendWith(MockitoExtension.class)
class FinalServiceTest {
    @Mock
    private FinalService finalService; // 直接 @Mock,不會(huì)報(bào)錯(cuò)!
    @Test
    void testFinalClass() {
        when(finalService.sayHello()).thenReturn("Mocked Hello");
        assertEquals("Mocked Hello", finalService.sayHello());
    }
}

二、?? Spy 是啥?解決什么問(wèn)題?

1. 核心概念:部分模擬

  • Mock(完全模擬) :創(chuàng)建一個(gè)空殼對(duì)象。所有方法默認(rèn)都不執(zhí)行真實(shí)代碼,直接返回 null 或 0。你必須手動(dòng)定義每一個(gè)方法的行為。

  • Spy(部分模擬) :包裝一個(gè)真實(shí)的對(duì)象。

    • 默認(rèn)情況下,它會(huì)執(zhí)行真實(shí)的代碼。
    • 只有當(dāng)你明確告訴它“這個(gè)方法要攔截”時(shí),它才會(huì)返回假數(shù)據(jù)。

2. 解決什么問(wèn)題?

  • 場(chǎng)景一:遺留代碼或復(fù)雜對(duì)象。當(dāng)你有一個(gè)類,方法很多,你只想 Mock 其中一個(gè)很難測(cè)的方法(比如調(diào)用了外部 API),而其他方法邏輯很復(fù)雜你不想重寫(xiě),這時(shí)用 Spy 最省事。
  • 場(chǎng)景二:驗(yàn)證真實(shí)調(diào)用。你想確保某個(gè)方法被調(diào)用了,同時(shí)還想驗(yàn)證它執(zhí)行后的真實(shí)副作用。

??? 怎么用?(核心語(yǔ)法)

在 Spring Boot 項(xiàng)目中,我們通常分兩種情況使用 Spy:純單元測(cè)試 和 Spring 容器集成測(cè)試。

1. 純單元測(cè)試(使用@Spy)

這是 Mockito 的原生用法,用于測(cè)試普通的 Java 類。

關(guān)鍵點(diǎn):使用 Spy 時(shí),存根語(yǔ)法(Stubbing)必須換!

  • Mock 用:when(mock.method()).thenReturn(...)
  • Spy 用:doReturn(...).when(spy).method()
    • 為什么?因?yàn)?Spy 默認(rèn)執(zhí)行真實(shí)方法,如果用 when(spy.method()),真實(shí)方法會(huì)立即執(zhí)行,可能導(dǎo)致空指針異常。
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
    // 1. 必須初始化真實(shí)對(duì)象!不能寫(xiě) @Spy private UserService userService; (這樣會(huì)報(bào)空指針)
    @Spy
    private UserService userService = new UserService(); 
    @Mock
    private UserRepository userRepository;
    @Test
    void testSpyUsage() {
        // --- Arrange ---
        // 假設(shè) UserService 有個(gè)方法 calculateTax() 很復(fù)雜,我們想 Mock 它
        // 注意語(yǔ)法:doReturn(...).when(spy).method()
        doReturn(100.0).when(userService).calculateTax();
        // --- Act ---
        // 調(diào)用其他未 Mock 的方法,會(huì)執(zhí)行真實(shí)邏輯
        // 調(diào)用 calculateTax,會(huì)返回 100.0
        double tax = userService.calculateTax();
        // --- Assert ---
        assertEquals(100.0, tax);
        // --- Verify ---
        // 驗(yàn)證真實(shí)方法是否被調(diào)用
        verify(userService, times(1)).calculateTax();
    }
}

2. Spring 集成測(cè)試(使用@SpyBean)

當(dāng)你使用 @SpringBootTest 時(shí),普通的 @Spy 無(wú)法替換 Spring 容器里的 Bean。這時(shí)要用 Spring Boot 提供的  @SpyBean。

作用:把 Spring 容器里原本的 Bean 替換成一個(gè) Spy 對(duì)象。

@SpringBootTest
class OrderServiceIntegrationTest {
    @Autowired
    private OrderService orderService; // 真實(shí)的 Service
    // 1. @SpyBean:替換容器里的 UserService,但保留真實(shí)邏輯
    @SpyBean 
    private UserService userService;
    @Test
    void testOrderWithSpyBean() {
        // --- Arrange ---
        // 攔截 getUserLevel 方法,返回 "VIP"
        doReturn("VIP").when(userService).getUserLevel(anyLong());
        // --- Act ---
        // 調(diào)用 orderService,它會(huì)調(diào)用 userService.getUserLevel
        // 此時(shí) getUserLevel 返回 "VIP",但 userService 的其他方法(如 saveUser)仍走真實(shí)數(shù)據(jù)庫(kù)邏輯(如果配置了的話)
        orderService.createOrder(1L);
        // --- Verify ---
        // 驗(yàn)證 getUserLevel 確實(shí)被調(diào)用了
        verify(userService).getUserLevel(1L);
    }
}

?? Mock vs Spy:怎么選?

為了讓你更清晰地做決定,我整理了這個(gè)對(duì)比表:

維度@Mock (完全模擬)@Spy / @SpyBean (部分模擬)
真實(shí)代碼執(zhí)行絕不執(zhí)行默認(rèn)執(zhí)行 (除非被攔截)
初始化要求不需要實(shí)例化必須有真實(shí)實(shí)例 (new Object())
存根語(yǔ)法when(mock.method())...doReturn(...).when(spy)...
風(fēng)險(xiǎn)低(完全隔離)中(真實(shí)代碼可能拋異常或依賴數(shù)據(jù)庫(kù))
適用場(chǎng)景依賴對(duì)象(Repository, Client)被測(cè)對(duì)象本身(想測(cè)部分邏輯)、遺留代碼

?? Spy vs InjectMocks

維度@Spy@InjectMocks
核心職責(zé)部分模擬。包裝一個(gè)真實(shí)對(duì)象,保留真實(shí)邏輯,但允許攔截特定方法。依賴注入。創(chuàng)建被測(cè)對(duì)象,并自動(dòng)把 @Mock 或 @Spy 塞進(jìn)去。
代碼行為默認(rèn)執(zhí)行真實(shí)代碼。負(fù)責(zé)初始化對(duì)象(通過(guò)構(gòu)造函數(shù)或字段注入)。
語(yǔ)法陷阱必須手動(dòng)初始化實(shí)例(= new UserService()),否則報(bào)錯(cuò)。不需要手動(dòng)初始化,Mockito 會(huì)自動(dòng)幫你 new 出來(lái)。
常用搭配用于被測(cè)對(duì)象本身(當(dāng)你不想 Mock 所有方法時(shí))。用于被測(cè)對(duì)象(當(dāng)你想完全隔離,只測(cè)邏輯流轉(zhuǎn)時(shí))。
存根語(yǔ)法必須用 doReturn(...).when(spy)...(它本身不存根,它注入的對(duì)象如果是 Mock,則用 when...thenReturn)

?? 避坑指南

  1. 初始化陷阱:使用 @Spy 時(shí),字段必須手動(dòng)初始化(如 = new UserService()),否則 Mockito 無(wú)法創(chuàng)建 Spy 對(duì)象,會(huì)報(bào) NullPointerException。
  2. Final 方法:和 Mock 一樣,Spy 也無(wú)法 Spy final 方法。調(diào)用 final 方法時(shí),永遠(yuǎn)執(zhí)行真實(shí)代碼,無(wú)法攔截。
  3. 自調(diào)用問(wèn)題:在 Spring 中,如果一個(gè) Bean 的方法 A 調(diào)用了同一個(gè)類的方法 B(this.methodB()),即使你 Spy 了方法 B,A 調(diào)用 B 時(shí)走的也是真實(shí)邏輯,Spy 的攔截可能失效(因?yàn)?Spring AOP 代理機(jī)制)。

總結(jié)建議:在單元測(cè)試中,優(yōu)先使用 @Mock,因?yàn)樗蓛簟⒏踩?。只有?dāng)你真的需要保留真實(shí)邏輯,或者為了省事不想 Mock 所有依賴時(shí),才使用  @Spy。

到此這篇關(guān)于Springboot結(jié)合Junit單元測(cè)試的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Springboot Junit單元測(cè)試內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java中xml和對(duì)象之間的互相轉(zhuǎn)換方法

    java中xml和對(duì)象之間的互相轉(zhuǎn)換方法

    在java開(kāi)發(fā)中我們經(jīng)常會(huì)遇到Xml與對(duì)象互相轉(zhuǎn)換的情況,這篇文章主要給大家介紹了關(guān)于java中xml和對(duì)象之間的互相轉(zhuǎn)換方法,文中給出了兩種解決方法,需要的朋友可以參考下
    2023-06-06
  • Java深入淺出講解多線程的概念到使用

    Java深入淺出講解多線程的概念到使用

    哈哈!經(jīng)過(guò)一個(gè)階段的學(xué)習(xí),Java基礎(chǔ)知識(shí)學(xué)習(xí)終于到多線程了!Java多線程以及后面互斥鎖的概念都是Java基礎(chǔ)學(xué)習(xí)的難點(diǎn),所以我做了一個(gè)總結(jié),希望對(duì)大家也有幫助
    2022-05-05
  • 一文帶你解讀Java序列化和反序列化機(jī)制

    一文帶你解讀Java序列化和反序列化機(jī)制

    序列化是將對(duì)象轉(zhuǎn)換成二進(jìn)制字節(jié)流的過(guò)程;反序列化是從二進(jìn)制字節(jié)流中恢復(fù)對(duì)象的過(guò)程。本文將大家詳細(xì)講講二者的原理與實(shí)現(xiàn),希望對(duì)大家有所幫助
    2023-01-01
  • Springboot實(shí)現(xiàn)WebMvcConfigurer接口定制mvc配置詳解

    Springboot實(shí)現(xiàn)WebMvcConfigurer接口定制mvc配置詳解

    這篇文章主要介紹了Springboot實(shí)現(xiàn)WebMvcConfigurer接口定制mvc配置詳解,spring?boot拋棄了傳統(tǒng)xml配置文件,通過(guò)配置類(標(biāo)注@Configuration的類,@Configuration配置類相當(dāng)于一個(gè)xml配置文件)以JavaBean形式進(jìn)行相關(guān)配置,需要的朋友可以參考下
    2023-09-09
  • java引用jpython的方法示例

    java引用jpython的方法示例

    這篇文章主要介紹了java引用jpython的方法,結(jié)合實(shí)例形式分析了java引用jpython及相關(guān)使用技巧,需要的朋友可以參考下
    2016-11-11
  • Java?guava框架LoadingCache及CacheBuilder本地小容量緩存框架總結(jié)

    Java?guava框架LoadingCache及CacheBuilder本地小容量緩存框架總結(jié)

    Guava?Cache本地緩存框架主要是一種將本地?cái)?shù)據(jù)緩存到內(nèi)存中,但數(shù)據(jù)量并不能太大,否則將會(huì)占用過(guò)多的內(nèi)存,本文給大家介紹Java?guava框架?LoadingCache及CacheBuilder?本地小容量緩存框架總結(jié),感興趣的朋友一起看看吧
    2023-12-12
  • 如何通過(guò)一張圖搞懂springBoot自動(dòng)注入原理

    如何通過(guò)一張圖搞懂springBoot自動(dòng)注入原理

    這篇文章主要給大家介紹了關(guān)于如何通過(guò)一張圖搞懂springBoot自動(dòng)注入原理的相關(guān)資料,文中通過(guò)圖文以及實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2022-02-02
  • 解決hive啟動(dòng)時(shí)java.net.ConnectException:拒絕連接的問(wèn)題

    解決hive啟動(dòng)時(shí)java.net.ConnectException:拒絕連接的問(wèn)題

    Hadoop集群連接被拒,需檢查集群是否啟動(dòng)、關(guān)閉防火墻/SELinux、確認(rèn)安全模式退出,若問(wèn)題仍存,查看日志報(bào)錯(cuò)處理
    2025-08-08
  • Java利用過(guò)濾器實(shí)現(xiàn)完善登錄功能

    Java利用過(guò)濾器實(shí)現(xiàn)完善登錄功能

    這篇文章主要為大家詳細(xì)介紹了Java如何利用過(guò)濾器實(shí)現(xiàn)完善登錄功能,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Java有一定幫助,需要的可以參考一下
    2022-09-09
  • springboot配置redis過(guò)程詳解

    springboot配置redis過(guò)程詳解

    這篇文章主要介紹了springboot配置redis過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-09-09

最新評(píng)論

开鲁县| 玉林市| 秦安县| 乳山市| 彰化县| 龙里县| 德惠市| 上思县| 衡阳市| 青冈县| 武定县| 襄汾县| 奉新县| 内黄县| 友谊县| 芮城县| 海兴县| 织金县| 武定县| 南投县| 岚皋县| 瓦房店市| 青川县| 图片| 霞浦县| 安平县| 沧州市| 洛川县| 安多县| 通州市| 晴隆县| 南平市| 靖远县| 东莞市| 樟树市| 那曲县| 米泉市| 荆门市| 巴马| 武汉市| 哈密市|