Spring Boot單元測試
Spring 框架提供了一個專門的測試模組(spring-test
),用於應用程式的整合測試。 在 Spring Boot 中,你可以通過spring-boot-starter-test
啟動器快速開啟和使用它。
# pom.xml
12345 | <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope></dependency |
1. Junit 測試
當你的單元測試程式碼不需要用到 Spring Boot 功能,而只是一個簡單的測試時,你可以直接編寫你的 Junit 測試程式碼:
12345678 | public class SimpleJunitTest { public void testSayHi() { System.out.println("Hi Junit."); }} |
2. Spring Boot 測試
當你的整合測試程式碼需要用到 Spring Boot 功能時,你可以使用@SpringBootTest
註解。
該註解是普通的 Spring 專案(非 Spring Boot 專案)中編寫整合測試程式碼所使用的@ContextConfiguration
12345678910111213 | public class BeanInjectTest { private HelloService helloService; public void testSayHi() { System.out.println(helloService.sayHi()); }} | (SpringRunner.class)
123456789101112 | public class |
當執行 Spring Boot 應用程式測試時,它會自動的從當前測試類所在的包起一層一層向上搜尋,直到找到一個@SpringBootApplication
或@SpringBootConfiguration
註釋類為止。以此來確定如何裝載 Spring 應用程式的上下文資源。只要你以合理的方式組織你的程式碼,你專案的主配置通常是可以被發現的。本示例專案的部分檔案結構圖為:
123456789101112131415 | spring-boot-testing-sample\__ src \__ main : \__ java : \__ org : \__ fanlychie : |__ Application.java : \__ service : |__ HelloService.java \__ test \__ java \__ org \__ fanlychie \__ test |__ BeanInjectTest.java |
其中,主配置啟動類的程式碼為:
12345678 | public class Application { public static void main(String[] args) { SpringApplication.run(Application.class); }} |
如果搜尋演算法搜尋不到你專案的主配置檔案,將報出異常:
java.lang.IllegalStateException: Unable to find a @SpringBootConfiguration, you need to use @ContextConfiguration or @SpringBootTest(classes=…) with your test解決辦法是,按 Spring Boot 的約定重新組織你的程式碼結構,或者手工指定你要裝載的主配置檔案:
1234567 | public class BeanInjectTest { // ...} | (SpringRunner.class) (classes = {YourApplication.class})
基於 Spring 環境的 Junit 整合測試還需要使用@RunWith(SpringJUnit4ClassRunner.class)
註解,該註解能夠改變 Junit 並讓其執行在 Spring 的測試環境,以得到 Spring 測試環境的上下文支援。否則,在 Junit 測試中,Bean 的自動裝配等註解將不起作用。但由於 SpringJUnit4ClassRunner 不方便記憶,Spring 4.3 起提供了一個等同於 SpringJUnit4ClassRunner 的類 SpringRunner,因此可以簡寫成:@RunWith(SpringRunner.class)
。
3. Spring MVC 測試
當你想對 Spring MVC 控制器編寫單元測試程式碼時,可以使用@WebMvcTest
註解。它提供了自配置的 MockMvc,可以不需要完整啟動 HTTP 伺服器就可以快速測試 MVC 控制器。
12345678910111213141516171819 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*;import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; (SpringRunner.class) (HelloController.class)public class HelloControllerTest { private MockMvc mvc; public void testHello() throws Exception { mvc.perform(get("/hello")) .andExpect(status().isOk()) .andDo(print()); }} |
12345678910 | public class HelloController { ("/hello") public String hello(ModelMap model) { model.put("message", "Hello Page"); return "hello"; }} |
使用@WebMvcTest
註解時,只有一部分的 Bean 能夠被掃描得到,它們分別是:
@Controller
@ControllerAdvice
@JsonComponent
Filter
WebMvcConfigurer
HandlerMethodArgumentResolver
其他常規的@Component
(包括@Service
、@Repository
等)Bean 則不會被載入到 Spring 測試環境上下文中。
如果測試的 MVC 控制器中需要@Component
Bean 的參與,你可以使用@MockBean
註解來協助完成:
12345678910111213141516171819202122232425 | import static org.mockito.BDDMockito.*;import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*;import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; (SpringRunner.class) (HelloController.class)public class HelloControllerTest { private MockMvc mvc; private HelloService helloService; public void testSayHi() throws Exception { // 模擬 HelloService.sayHi() 呼叫, 返回 "=== Hi ===" when(helloService.sayHi()).thenReturn("=== Hi ==="); mvc.perform(get("/hello/sayHi")) .andExpect(status().isOk()) .andDo(print()); }} |
12345678910111213 | public class HelloController { private HelloService helloService; ("/hello/sayHi") public String sayHi(ModelMap model) { model.put("message", helloService.sayHi()); return "hello"; }} |
4. Spring Boot Web 測試
當你想啟動一個完整的 HTTP 伺服器對 Spring Boot 的 Web 應用編寫測試程式碼時,可以使用@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
註解開啟一個隨機的可用埠。Spring Boot 針對 REST 呼叫的測試提供了一個 TestRestTemplate 模板,它可以解析連結伺服器的相對地址。
1234567891011121314 | public class ApplicationTest { private TestRestTemplate restTemplate; public void testSayHello() { Map<String, Object> result = restTemplate.getForObject("/hello/sayHello", Map.class); System.out.println(result.get("message")); }} | (SpringRunner.class) (webEnvironment = WebEnvironment.RANDOM_PORT)
1234567891011121314 | public class HelloController { private HelloService helloService; ("/hello/sayHello") public Object helloInfo() { Map<String, Object> map = new HashMap<>(); map.put("message", helloService.sayHello()); return map; }} |
5. Spring Data JPA 測試
當你想對 Spring Data JPA 應用進行單元測試時,你可以使用@DataJpaTest
註解。並且在進行 JPA 測試時,你可以選擇使用記憶體資料庫還是真實的資料庫測試。
5.1 記憶體資料庫測試
預設情況下,@DataJpaTest
使用的是記憶體資料庫進行測試,你無需配置和啟用真實的資料庫。只需要在 pom.xml 配置檔案中宣告如下依賴即可:
# pom.xml
1234 | <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId></dependency> |
@DataJpaTest
註解它只掃描@Entity
Bean 和裝配 Spring Data JPA 儲存庫,其他常規的@Component
(包括@Service
、@Repository
等)Bean 則不會被載入到 Spring 測試環境上下文。
123456789101112131415161718 | public class UserRepositoryInMemoryTest { private UserRepository userRepository; | (SpringRunner.class)