您現在的位置是:首頁 > 運動

單元測試不再怕!一文學會SpringBoot單元測試

由 程式yuan 發表于 運動2022-01-12
簡介註解名稱註解描述@SpringBootTestSpring Boot 應用程式專用的測試註解@RunWithJUnit 框架提供的用於設定測試執行器的基礎註解@TestJUnit 中使用的基礎測試註解,用來表明需要執行的測試用例@DataJ

springboot測試類怎麼寫

單元測試不再怕!一文學會SpringBoot單元測試

註解相關

@SpringBootTest

SpringBoot應用程式專用的測試註解,SpringBoot程式的入口是Bootstrap類,所以SpringBoot專門提供了一個@SpringBootTest註解測試Bootstrap類。該註解也可以引用Bootstrap類的配置,因為所有配置都會透過Bootstrap類去載入。

在 Spring Boot 中,@SpringBootTest 註解主要用於測試基於自動配置的 ApplicationContext,它允許我們設定測試上下文中的 Servlet 環境。

@SpringBootTest註解中的webEnvironment可以有四個選項:

MOCK:載入 WebApplicationContext 並提供一個 Mock 的 Servlet 環境,此時內建的 Servlet 容器並沒有正式啟動。

RANDOM_PORT:載入 EmbeddedWebApplicationContext 並提供一個真實的 Servlet 環境,然後使用一個隨機埠啟動內建容器。

DEFINED_PORT:這個配置也是透過載入 EmbeddedWebApplicationContext 提供一個真實的 Servlet 環境,但使用的是預設埠,如果沒有配置埠就使用 8080。

NONE:載入 ApplicationContext 但並不提供任何真實的 Servlet 環境。

在多數場景下,一個真實的 Servlet 環境對於測試而言過於重量級,透過 MOCK 環境則可以緩解這種環境約束所帶來的困擾

@RunWith註解與SpringRunner

JUnit框架提供的用於設定測試執行器的基礎註解。

我們可以透過 @RunWith(SpringRunner。class) 讓測試運行於Spring測試環境。

SpringRunner 繼承 SpringJUnit4ClassRunner 其實他們是一樣的,就是執行在spring環境,它允許 JUnit 和 Spring TestContext 整合執行,而 Spring TestContext 則提供了用於測試 Spring 應用程式的各項通用的支援功能。

@Test

JUnit 中使用的基礎測試註解,用來表明需要執行的測試用例

@DataJpaTest

專門用於測試關係型資料庫的測試註解

@DataJpaTest 註解會自動注入各種 Repository 類,即是持久化bean,並初始化一個記憶體資料庫和及訪問該資料庫的資料來源。

在測試場景下,一般我們可以使用 H2 作為記憶體資料庫,並透過 MySQL 實現資料持久化

@MockBean

用於實現Mock機制的測試註解

自動用 Mockito 模擬替換應用程式上下文中相同型別的 bean。

@WebMvcTest

在 Web 容器環境中嵌入 MockMvc 的註解

@SpringBootTest 註解不能和 @WebMvcTest 註解同時使用。

該註解將初始化測試 Controller 所必需的 Spring MVC 基礎設施

controllers 引數設定為 XxxController。class,告訴 Spring Boot 將為此測試建立的應用程式上下文限制為給定的控制器 bean 和該bean相關的一些 Spring Web MVC 所需的一些框架 bean。

MockMvc類提供的基礎方法分為以下6 種

Perform:執行一個 RequestBuilder 請求,會自動執行 SpringMVC 流程並對映到相應的 Controller 進行處理。

get/post/put/delete:聲明發送一個 HTTP 請求的方式,根據 URI 模板和 URI 變數值得到一個 HTTP 請求,支援 GET、POST、PUT、DELETE 等 HTTP 方法。

param:新增請求引數,傳送 JSON 資料時將不能使用這種方式,而應該採用 @ResponseBody 註解。

andExpect:新增 ResultMatcher 驗證規則,透過對返回的資料進行判斷來驗證 Controller 執行結果是否正確。

andDo:新增 ResultHandler 結果處理器,比如除錯時列印結果到控制檯。

andReturn:最後返回相應的 MvcResult,然後執行自定義驗證或做非同步處理。

@AutoConfigureMockMvc

@AutoConfigureMockMvc與@SpringBootTest 組合嵌入MockMvc的註解,在使用 @SpringBootTest 註解的場景下,如果我們想使用 MockMvc 物件,那麼可以引入 @AutoConfigureMockMvc 註解。

透過將 @SpringBootTest 註解與 @AutoConfigureMockMvc 註解相結合,@AutoConfigureMockMvc 註解將透過 @SpringBootTest 載入的 Spring 上下文環境中自動配置 MockMvc 這個類。

註解名稱

註解描述

@SpringBootTest

Spring Boot 應用程式專用的測試註解

@RunWith

JUnit 框架提供的用於設定測試執行器的基礎註解

@Test

JUnit 中使用的基礎測試註解,用來表明需要執行的測試用例

@DataJpaTest

專門用於測試關係型資料庫的測試註解

@MockBea

用於實現 Mock 機制的測試註解

@WebMvcTest

在 Web 容器環境中嵌入 MockMvc 的註解

@AutoConfigureMockMvc

與@SpringBootTest組合嵌入MockMvc的註解

相關說明

TestEntityManager

它的效果相當於不使用真正的 XxxRepository(即是持久化bean) 完成資料的持久化,從而提供了一種資料與環境之間的隔離機制。

TestRestTemplate

Spring Boot 提供的 TestRestTemplate 發起請求的方式與 RestTemplate完全一致,只不過它專門用在測試環境中。

如果我們想在測試環境中使用 @SpringBootTest,則可以直接使用 TestRestTemplate 來測試遠端訪問過程

@SpringBootTest 註解透過使用 SpringBootTest。WebEnvironment。RANDOM_PORT 指定了隨機埠的 Web 執行環境。然後,我們基於 TestRestTemplate 發起了 HTTP 請求並驗證了結果。

Mock 機制

Mock 的意思是模擬,它可以用來對系統、元件或類進行隔離。

在測試過程中,我們通常關注測試物件本身的功能和行為,而對測試物件涉及的一些依賴,僅僅關注它們與測試物件之間的互動(比如是否呼叫、何時呼叫、呼叫的引數、呼叫的次數和順序,以及返回的結果或發生的異常等),並不關注這些被依賴物件如何執行這次呼叫的具體細節。因此,Mock 機制就是使用 Mock 物件替代真實的依賴物件,並模擬真實場景來開展測試工作

@SpringBootTest 註解中的 SpringBootTest。WebEnvironment。MOCK 選項,該選項用於載入WebApplicationContext 並提供一個 Mock 的 Servlet 環境,內建的 Servlet 容器並沒有真實啟動。

測試實戰

前提

初始化資料

DELETE FROM user;INSERT INTO user (id, name, age, email)VALUES (1, ‘Jone’, 18, ‘test1@baomidou。com’), (2, ‘Jack’, 20, ‘test2@baomidou。com’), (3, ‘Tom’, 28, ‘test3@baomidou。com’), (4, ‘Sandy’, 21, ‘test4@baomidou。com’), (5, ‘Billie’, 24, ‘test5@baomidou。com’);複製程式碼

pom依賴

<!——我們需要一個web應用——> org。springframework。boot spring-boot-starter-web<!——簡化——> org。projectlombok lombok true<!—— springboot測試模組——> org。springframework。boot spring-boot-starter-test test<!—— 我們希望在測試環境時候能把資料庫環境隔離,用記憶體資料庫——> com。h2database h2 runtime<!——我們希望開發環境時候使用 mysql資料庫——> mysql mysql-connector-java<!—— 持久化框架——> org。springframework。boot spring-boot-starter-data-jpa<!—— junit測試元件——> junit junit test複製程式碼

注意:在src/main/resources/下放置mysql的配置,而在src/test/resources/下放置h2的配置,springboot自動幫助我們在不同的環境執行不同的配置檔案

單元測試不再怕!一文學會SpringBoot單元測試

實體類

/** * @Description: 實體類 * GenerationType: * TABLE:使用一個特定的資料庫表格來儲存主鍵。 * SEQUENCE:根據底層資料庫的序列來生成主鍵,條件是資料庫支援序列。 * IDENTITY:主鍵由資料庫自動生成(主要是自動增長型) * AUTO:主鍵由程式控制選擇上面的一個策略。 * @Author: jianweil * @date: 2021/11/22 17:29 */@Entity@AllArgsConstructor@NoArgsConstructor@Data//@Proxy(lazy = false)public class User { @Id //配置了h2GenerationType。AUTO程式幫助我們使用了GenerationType。SEQUENCE //@GeneratedValue(strategy = GenerationType。AUTO) //序列化 //@GeneratedValue(strategy = GenerationType。SEQUENCE) // https://www。cnblogs。com/hongchengshise/p/10612301。html //資料庫自增 @GeneratedValue(strategy = GenerationType。IDENTITY) private Long id; @Column private String name; @Column private Integer age; @Column private String email;}複製程式碼

測試持久層-dao

方法1。 @DataJpaTest

測試持久化類我們一般使用@DataJpaTest+@RunWith(SpringRunner。class)註解

@DataJpaTest 註解會自動注入各種 Repository 類,並初始化一個記憶體資料庫和及訪問該資料庫的資料來源。

在測試場景下,用@DataJpaTest 註解的測試使用嵌入式記憶體資料庫。我們可以使用 H2 作為記憶體資料庫

它與@RunWith(SpringRunner。class)結合使用。 該註解會禁用完全自動配置,並且僅應用與 JPA 測試相關的配置。

由於H2啟動初始化5條記錄到記憶體資料庫,所以user1插入的id為6

@RunWith(SpringRunner。class)@DataJpaTestpublic class TestDao { /** * 效果相當於不使用真正的 UserRepository 完成資料的持久化,從而提供了一種資料與環境之間的隔離機制。 */ @Autowired private TestEntityManager entityManager; @Autowired private UserRepository userRepository; /** * @throws Exception */ @Test public void testGetById() throws Exception { String expected = “new6@qq。com”; User user1 = new User(); user1。setName(“new6”); user1。setAge(6); user1。setEmail(expected); //User user3 = this。userRepository。save(user1); //User的id自增策略 IDENTITY策略:用初始化好資料的h2資料庫儲存 id=6 //User的id自增策略 SEQUENCE策略:使用新的空的h2資料庫儲存 id=1 //與上面效果一樣 this。entityManager。persist(user1); User user2 = new User(); user2。setName(“new7”); user2。setAge(7); user2。setEmail(“new7@qq。com”); //User user4 = this。userRepository。save(user2); this。entityManager。persist(user2); User user6 = this。userRepository。getById(6L); User user7 = this。userRepository。getById(7L); Assert。assertNotNull(user6); Assert。assertEquals(expected, user6。getEmail()); Assert。assertEquals(“new7@qq。com”, user7。getEmail()); }}複製程式碼

測試業務層-Service

方法1。 使用Mock機制模擬資料

Mock 機制就是使用 Mock 物件替代真實的依賴物件,並模擬真實場景來開展測試工作。

@SpringBootTest 註解中的 SpringBootTest。WebEnvironment。MOCK 選項,該選項用於載入WebApplicationContext 並提供一個 Mock 的 Servlet 環境,內建的 Servlet 容器並沒有真實啟動。

首先,我們透過 @MockBean 註解注入了 userRepository,即是告訴程式我準備虛擬這個類

然後,基於第三方 Mock 框架 Mockito 提供的 when/thenReturn 機制完成了對 userRepository 中 getById() 方法的 Mock。即是告訴程式我虛擬這個類的這個方法了,你不要去真實資料庫查了,用我這個返回

@RunWith(SpringRunner。class)@SpringBootTest(webEnvironment = SpringBootTest。WebEnvironment。MOCK)public class TestServiceMock { /** * @MockBean使資料隔離 */ @MockBean private UserRepository userRepository; @Resource private UserService userService; @Test public void testGetById() throws Exception { Long id = 22L; //用Mock構建測試方法userService。findById(22L)需要的userRepository。getById(id)的資料, Mockito。when(userRepository。getById(id))。thenReturn(new User(22L, “MockBean”, 22, “MockBean@qq。com”)); User user = userService。findById(22L); Assert。assertNotNull(user); Assert。assertEquals(“MockBean”, user。getName()); }}複製程式碼

方法2。 直接測試資料庫

如果你希望在測試用例中直接注入真實的userRepository,這時就可以使用@SpringBootTest 註解中的 SpringBootTest。WebEnvironment。RANDOM_PORT 選項

SpringBootTest。WebEnvironment。RANDOM_PORT:以一個隨機的埠啟動整個 Spring Boot 工程,並從資料庫中(H2)真實獲取目標資料進行驗證。

@RunWith(SpringRunner。class)@SpringBootTest(webEnvironment = SpringBootTest。WebEnvironment。RANDOM_PORT)public class TestServiceNoMock { /** * 注入真正資料庫 */ @Resource private UserService userService; /** * 報錯沒有seesion是jpa框架導致,解決有: * 1。 加事務註解 @Transactional * 2。 實體類加@Proxy(lazy = false) * 3。 配置檔案加spring。jpa。properties。hibernate。enable_lazy_load_no_trans=true * * @throws Exception */ @Test @Transactional public void testGetById() throws Exception { Long id = 1L; User user = userService。findById(id); Assert。assertNotNull(user); Assert。assertEquals(“Jone”, user。getName()); }}複製程式碼

測試控制層-Controller

方法1。@SpringBootTest+TestRestTemplate+ @MockBean

@SpringBootTest 註解透過使用 SpringBootTest。WebEnvironment。RANDOM_PORT 指定了隨機埠的 Web 執行環境。

如果我們想在測試環境中使用 @SpringBootTest,則可以直接使用 TestRestTemplate 來測試遠端訪問過程

Spring Boot 提供的 TestRestTemplate 與 RestTemplate 非常類似,只不過它專門用在測試環境中。

@RunWith(SpringRunner。class)@SpringBootTest(webEnvironment = SpringBootTest。WebEnvironment。RANDOM_PORT)public class TestController { @Autowired private TestRestTemplate testRestTemplate; @MockBean private UserService userService; @Test public void testGetById() throws Exception { Long userId = 1L; given(this。userService。findById(userId)) 。willReturn(new User(1L, “MockBean”, 44, “MockBean@qq。com”)); //postForObject是post請求 ResponseEntity responseEntity = testRestTemplate。exchange(“/user/” + userId, HttpMethod。GET, null, User。class); Assert。assertEquals(“MockBean@qq。com”, responseEntity。getBody()。getEmail()); }}複製程式碼

方法2。@SpringBootTest+@AutoConfigureMockMvc+ @MockBean

在使用 @SpringBootTest 註解的場景下,如果我們想使用 MockMvc 物件,那麼可以引入 @AutoConfigureMockMvc 註解。

透過將 @SpringBootTest 註解與 @AutoConfigureMockMvc 註解相結合,@AutoConfigureMockMvc 註解將透過 @SpringBootTest 載入的 Spring 上下文環境中自動配置 MockMvc 這個類。

@RunWith(SpringRunner。class)@SpringBootTest@AutoConfigureMockMvcpublic class TestSpringBootTestController { @Autowired private MockMvc mvc; /** * 模擬的 */ @MockBean private UserService userService; @Test public void testGetById() throws Exception { Long userId = 55L; //模擬實現 given(this。userService。findById(userId)) 。willReturn(new User(55L, “MockBean”, 55, “MockBean@qq。com”)); this。mvc。perform(org。springframework。test。web。servlet。request。MockMvcRequestBuilders。get(“/user/” + userId)。accept(MediaType。APPLICATION_JSON))。andExpect(status()。isOk()); }}複製程式碼

方法3。@WebMvcTest+@MockBean

@SpringBootTest 註解不能和 @WebMvcTest 註解同時使用

@WebMvcTest(UserController。class) 該註解將初始化測試 UserController 所必需的 Spring MVC 基礎設施,即是相關bean,不是全部的bean都載入,如不寫則Spring Boot 將在應用程式上下文中包含所有控制器。

@RunWith(SpringRunner。class)@WebMvcTest(UserController。class)public class TestWebMvcTestController { @Autowired private MockMvc mvc; /** * 模擬的 */ @MockBean private UserService userService; @Test public void testGetById() throws Exception { Long userId = 55L; //模擬實現 given(this。userService。findById(userId)) 。willReturn(new User(55L, “MockBean”, 55, “TestWebMvcTestController@qq。com”)); this。mvc。perform(get(“/user/” + userId)。accept(MediaType。APPLICATION_JSON))。andExpect(status()。isOk()); }}

連結:https://juejin。cn/post/7036539195858354213

推薦文章