SpringBoot+JUnit5+MockMvc+Mockito單元測(cè)試的實(shí)現(xiàn)
今天聊聊如何在 SpringBoot 中集成 Junit5、MockMvc、Mocktio。Junit5 是在 Java 棧中應(yīng)用最廣的測(cè)試框架,Junit4 一度霸榜。
升級(jí)到 Junit5 之后,除了增加 Java8 的很多特性,做了很多功能增強(qiáng),在結(jié)構(gòu)上做了優(yōu)化調(diào)整,拆分了很多不同的模塊,可以按需引入,比如:
- JUnit Platform - 在 JVM 上啟動(dòng)測(cè)試框架
- JUnit Jupiter - 在 JUnit5 中編寫(xiě)測(cè)試和擴(kuò)展
- JUnit Vintage - 提供運(yùn)行基于 JUnit3 和 JUnit4 的測(cè)試引擎
從 SpringBoot 2.2.0 之后,Junit5 已經(jīng)成為了默認(rèn)的 Junit 版本。有了 JUnit Vintage,從 Junit4 遷移到 Junit5 的成本極低。所以本文就直接針對(duì) Junit5 開(kāi)始了。
版本
先說(shuō)版本,是為了避免因?yàn)榘姹静町惓霈F(xiàn)各種奇怪的問(wèn)題:
- JDK:jdk8(小版本可以忽略)
- SpringBoot:2.5.2
- 繼承spring-boot-starter-parent
- 依賴spring-boot-starter-web
- 依賴spring-boot-starter-test
- JUnit:5.7.2
- Mockito:3.9.0
- hamcrest:2.2
SpringBoot 的好處在于,只要繼承spring-boot-starter-parent或引入spring-boot-pom-dependencies,然后添加spring-boot-starter-test依賴即可。定義的 POM 內(nèi)容如下:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.2</version> </parent> <groupId>cn.howardliu.effective.spring</groupId> <artifactId>springboot-junit5-mockito</artifactId> <version>0.0.1-SNAPSHOT</version> <name>springboot-junit5-mockio</name> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
因?yàn)槔^承了spring-boot-starter-parent,所以我們依賴的spring-boot-starter-test不需要寫(xiě)具體的版本,可以直接集成父級(jí)的版本定義。其中,spring-boot-starter-web是用于提供 REST API 的 web 容器,spring-boot-starter-test可以提供各種測(cè)試框架的,spring-boot-maven-plugin是將 SpringBoot 應(yīng)用打包為可執(zhí)行 jar 的插件。
項(xiàng)目結(jié)構(gòu)
因?yàn)槭?DEMO 示例,我們實(shí)現(xiàn)一個(gè) Echo 接口,能夠接收請(qǐng)求參數(shù),并返回加工后的字符串。按照慣例,我們使用萬(wàn)能的Hello, World!。
我們的項(xiàng)目結(jié)構(gòu)如下:
├── pom.xml └── src ├── main │ ├── java │ │ └── cn │ │ └── howardliu │ │ └── effective │ │ └── spring │ │ └── springbootjunit5mockio │ │ ├── SpringbootJunit5MockioApplication.java │ │ ├── controller │ │ │ └── EchoController.java │ │ └── service │ │ ├── EchoService.java │ │ └── impl │ │ └── EchoServiceImpl.java │ └── resources │ └── application.yaml └── test └── java └── cn └── howardliu └── effective └── spring └── springbootjunit5mockio └── controller ├── EchoControllerMockTest.java └── EchoControllerNoMockitoTest.java
- SpringbootJunit5MockioApplication:SpringBoot 應(yīng)用啟動(dòng)入口
- EchoController:接口定義
- EchoService:實(shí)現(xiàn)業(yè)務(wù)邏輯接口
- EchoServiceImpl:接口實(shí)現(xiàn)
- EchoControllerMockTest:使用 Mock 代理 EchoService 實(shí)現(xiàn)
- EchoControllerNoMockitoTest:直接測(cè)試接口實(shí)現(xiàn)
EchoServiceImpl
我們看下EchoService的實(shí)現(xiàn),這將是我們 DEMO 的核心實(shí)現(xiàn):
@Service public class EchoServiceImpl implements EchoService { @Override public String echo(String foo) { return "Hello, " + foo; } }
EchoControllerNoMockitoTest
我們先使用 Junit5+MockMvc 實(shí)現(xiàn) Controller 接口的普通調(diào)用,代碼如下:
@SpringBootTest(classes = SpringbootJunit5MockioApplication.class) @AutoConfigureMockMvc class EchoControllerNoMockitoTest { @Autowired private MockMvc mockMvc; @Test void echo() throws Exception { final String result = mockMvc.perform( MockMvcRequestBuilders.get("/echo/") .param("name", "看山") ) .andExpect(MockMvcResultMatchers.status().isOk()) .andDo(MockMvcResultHandlers.print()) .andReturn() .getResponse() .getContentAsString(StandardCharsets.UTF_8); Assertions.assertEquals("Hello, 看山", result); } }
我們通過(guò)SpringBootTest注解定義這是一個(gè) SpringBoot 應(yīng)用的測(cè)試用例,然后通過(guò)AutoConfigureMockMvc啟動(dòng)測(cè)試容器。這樣,就可以直接注入MockMvc實(shí)例測(cè)試 Controller 接口。
這里需要注意一點(diǎn),網(wǎng)上很多教程會(huì)讓寫(xiě)@ExtendWith({SpringExtension.class})這樣一個(gè)注解,其實(shí)完全沒(méi)有必要。通過(guò)源碼我們可以知道,SpringBootTest注解已經(jīng)添加了ExtendWith。
EchoControllerMockTest
這個(gè)測(cè)試用例中,我們通過(guò) Mockito 組件代理EchoService的echo方法,代碼如下:
@SpringBootTest(classes = SpringbootJunit5MockioApplication.class) @ExtendWith(MockitoExtension.class) @AutoConfigureMockMvc class EchoControllerMockTest { @Autowired private MockMvc mockMvc; @MockBean private EchoService echoService; @BeforeEach void setUp() { Mockito.when(echoService.echo(Mockito.any())) .thenReturn("看山說(shuō):" + System.currentTimeMillis()); } @Test void echo() throws Exception { final String result = mockMvc.perform( MockMvcRequestBuilders.get("/echo/") .param("name", "看山的小屋") ) .andExpect(MockMvcResultMatchers.status().isOk()) .andDo(MockMvcResultHandlers.print()) .andReturn() .getResponse() .getContentAsString(StandardCharsets.UTF_8); Assertions.assertTrue(result.startsWith("看山")); } }
在這個(gè)示例中,我們需要注意@ExtendWith(MockitoExtension.class)注解,這個(gè)注解是用于引入MockBean的,我們通過(guò)對(duì)echo方法的攔截,使其返回我們定義好的響應(yīng)結(jié)果。這種方式是為了在多系統(tǒng)或者多功能測(cè)試時(shí),不需要真正調(diào)用接口。
比如,我們需要獲取用戶手機(jī)號(hào),通常在接口中會(huì)校驗(yàn)用戶有沒(méi)有登錄,我們就可以使用 Mockito 的能力代理登錄驗(yàn)證,使結(jié)果永遠(yuǎn)是 true。
到此這篇關(guān)于SpringBoot+JUnit5+MockMvc+Mockito單元測(cè)試的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)SpringBoot JUnit5 MockMvc Mockito單元測(cè)試內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java在ElasticSearch中使用LocalDatetime類型
最近在開(kāi)發(fā)一個(gè)搜索功能的需求的時(shí)候,遇到了LocalDatetime類型不能保存到ElasticSearch中的問(wèn)題,這篇文章主要介紹了Java在ElasticSearch中使用LocalDatetime類型2023-10-10java源碼解析之String類的compareTo(String otherString)方法
這篇文章主要給大家介紹了關(guān)于java源碼解析之String類的compareTo(String otherString)方法的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-09-09CAT分布式實(shí)時(shí)監(jiān)控系統(tǒng)使用詳解
這篇文章主要為大家介紹了CAT分布式實(shí)時(shí)監(jiān)控系統(tǒng)介紹詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03java LRU(Least Recently Used )詳解及實(shí)例代碼
這篇文章主要介紹了java LRU(Least Recently Used )詳解及實(shí)例代碼的相關(guān)資料,Java里面實(shí)現(xiàn)LRU緩存通常有兩種選擇,一種是使用LinkedHashMap,一種是自己設(shè)計(jì)數(shù)據(jù)結(jié)構(gòu),使用鏈表+HashMap,需要的朋友可以參考下2016-11-11java實(shí)現(xiàn)簡(jiǎn)單聊天室單人版
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)簡(jiǎn)單聊天室的單人版,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-07-07SpringBoot后端接口的實(shí)現(xiàn)(看這一篇就夠了)
這篇文章主要介紹了SpringBoot后端接口的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09解決dubbo注冊(cè)到zookeeper速度慢的問(wèn)題
這篇文章主要介紹了解決dubbo注冊(cè)到zookeeper速度慢的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-04-04