一、Spring Boot Test介紹
Spring Test與JUnit等其他測試框架結合起來,提供了便捷高效的測試手段。而Spring Boot Test 是在Spring Test之上的再次封裝,增加了切片測試,增強了mock能力。
整體上,Spring Boot Test支持的測試種類,大致可以分為如下三類:
- 單元測試:一般面向方法,編寫一般業務代碼時,測試成本較大。涉及到的注解有@Test。
- 切片測試:一般面向難於測試的邊界功能,介於單元測試和功能測試之間。涉及到的注解有@RunWith @WebMvcTest等。
- 功能測試:一般面向某個完整的業務功能,同時也可以使用切面測試中的mock能力,推薦使用。涉及到的注解有@RunWith @SpringBootTest等。
功能測試過程中的幾個關鍵要素及支撐方式如下:
- 測試運行環境:通過@RunWith 和 @SpringBootTest啟動spring容器。
- mock能力:Mockito提供了強大mock功能。
- 斷言能力:AssertJ、Hamcrest、JsonPath提供了強大的斷言能力。
二、快速開始
增加spring-boot-starter-test依賴,使用@RunWith和@SpringBootTest注解,即可開始測試。
添加依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
一旦依賴了spring-boot-starter-test,下面這些類庫將被一同依賴進去:
- JUnit:java測試事實上的標准,默認依賴版本是4.12(JUnit5和JUnit4差別比較大,集成方式有不同)。
- Spring Test & Spring Boot Test:Spring的測試支持。
- AssertJ:提供了流式的斷言方式。
- Hamcrest:提供了豐富的matcher。
- Mockito:mock框架,可以按類型創建mock對象,可以根據方法參數指定特定的響應,也支持對於mock調用過程的斷言。
- JSONassert:為JSON提供了斷言功能。
- JsonPath:為JSON提供了XPATH功能。
1. 單元測試
@RunWith(SpringRunner.class) @SpringBootTest public class SpringBootApplicationTests { @Autowired private UserService userService; @Test public void testAddUser() { User user = new User(); user.setName("john"); user.setAddress("earth"); userService.add(user); } }
@RunWith是Junit4提供的注解,將Spring和Junit鏈接了起來。假如使用Junit5,不再需要使用@ExtendWith注解,@SpringBootTest和其它@*Test默認已經包含了該注解。
@SpringBootTest替代了spring-test中的@ContextConfiguration注解,目的是加載ApplicationContext,啟動spring容器。
使用@SpringBootTest時並沒有像@ContextConfiguration一樣顯示指定locations或classes屬性,原因在於@SpringBootTest注解會自動檢索程序的配置文件,檢索順序是從當前包開始,逐級向上查找被@SpringBootApplication或@SpringBootConfiguration注解的類。
2. 功能測試
一般情況下,使用@SpringBootTest后,Spring將加載所有被管理的bean,基本等同於啟動了整個服務,此時便可以開始功能測試。
由於web服務是最常見的服務,且我們對於web服務的測試有一些特殊的期望,所以@SpringBootTest注解中,給出了webEnvironment參數指定了web的environment,該參數的值一共有四個可選值:
- MOCK:此值為默認值,該類型提供一個mock環境,可以和@AutoConfigureMockMvc或@AutoConfigureWebTestClient搭配使用,開啟Mock相關的功能。注意此時內嵌的服務(servlet容器)並沒有真正啟動,也不會監聽web服務端口。
- RANDOM_PORT:啟動一個真實的web服務,監聽一個隨機端口。
- DEFINED_PORT:啟動一個真實的web服務,監聽一個定義好的端口(從application.properties讀取)。
- NONE:啟動一個非web的ApplicationContext,既不提供mock環境,也不提供真實的web服務。
注:如果當前服務的classpath中沒有包含web相關的依賴,spring將啟動一個非web的ApplicationContext,此時的webEnvironment就沒有什么意義了。
3. 切片測試
所謂切片測試,官網文檔稱為 “slice” of your application,實際上是對一些特定組件的稱呼。這里的slice並非單獨的類(畢竟普通類只需要基於JUnit的單元測試即可),而是介於單元測試和集成測試中間的范圍。
slice是指一些在特定環境下才能執行的模塊,比如MVC中的Controller、JDBC數據庫訪問、Redis客戶端等,這些模塊大多脫離特定環境后不能獨立運行,假如spring沒有為此提供測試支持,開發者只能啟動完整服務對這些模塊進行測試,這在一些復雜的系統中非常不方便,所以spring為這些模塊提供了測試支持,使開發者有能力單獨對這些模塊進行測試。
通過@*Test開啟具體模塊的測試支持,開啟后spring僅加載相關的bean,無關內容不會被加載。
使用@WebMvcTest用來校驗controllers是否正常工作的示例:
@RunWith(SpringRunner.class) @WebMvcTest(IndexController.class) public class SpringBootTest { @Autowired private MockMvc mvc; @Test public void testExample() throws Exception { //groupManager訪問路徑 //param傳入參數 MvcResult result=mvc.perform(MockMvcRequestBuilders.post("/groupManager").param("pageNum","1").param("pageSize","10")).andReturn(); MockHttpServletResponse response = result.getResponse(); String content = response.getContentAsString(); List<JtInfoDto> jtInfoDtoList = GsonUtils.toObjects(content, new TypeToken<List<JtInfoDto>>() {}.getType()); for(JtInfoDto infoDto : jtInfoDtoList){ System.out.println(infoDto.getJtCode()); } } }
使用@WebMvcTest和MockMvc搭配使用,可以在不啟動web容器的情況下,對Controller進行測試(注意:僅僅只是對controller進行簡單的測試,如果Controller中依賴用@Autowired注入的service、dao等則不能這樣測試)。
三、注解詳解
Spring為了避免的繁瑣難懂的xml配置,引入大量annotation進行系統配置,確實減輕了配置工作量。由此,理解這些annotation變得尤為重要,一定程度上講,對Spring Boot Test的使用,就是對其相關annotation的使用。
1. 按功能分類
從功能上講,Spring Boot Test中的注解主要分如下幾類:
- 配置類型:
@TestConfiguration
等。提供一些測試相關的配置入口。 - mock類型:
@MockBean
等。提供mock支持。 - 啟動測試類型:@SpringBootTest。以Test結尾的注解,具有加載applicationContext的能力。
- 自動配置類型:
@AutoConfigureJdbc
等。以AutoConfigure開頭的注解,具有加載測試支持功能的能力。
(1) 配置類型的注解
- @TestComponent:該注解是另一種
@Component
,在語義上用來指定某個Bean是專門用於測試的。該注解適用於測試代碼和正式混合在一起時,不加載被該注解描述的Bean,使用不多。 - @TestConfiguration:該注解是另一種
@TestComponent
,它用於補充額外的Bean或覆蓋已存在的Bean。在不修改正式代碼的前提下,使配置更加靈活。 - @TypeExcludeFilters:用來排除@TestConfiguration和@TestComponent。適用於測試代碼和正式代碼混合的場景,使用不多。
- @OverrideAutoConfiguration:可用於覆蓋
@EnableAutoConfiguration
,與ImportAutoConfiguration
結合使用,以限制所加載的自動配置類。在不修改正式代碼的前提下,提供了修改配置自動配置類的能力。 - @PropertyMapping:定義
@AutoConfigure*
注解中用到的變量名稱,例如在@AutoConfigureMockMvc
中定義名為spring.test.mockmvc.webclient.enabled的變量。一般不使用。
使用@SpringBootApplication
啟動測試或者生產代碼,被@TestComponent
描述的Bean會自動被排除掉。如果不是則需要向@SpringBootApplication
添加TypeExcludeFilter。
(2) mock類型的注解
- @MockBean:用於mock指定的class或被注解的屬性。
- @MockBeans:使@MockBean支持在同一類型或屬性上多次出現。
- @SpyBean:用於spy指定的class或被注解的屬性。
- @SpyBeans:使@SpyBean支持在同一類型或屬性上多次出現
@MockBean
和@SpyBean
這兩個注解,在mockito框架中本來已經存在,且功能基本相同。Spring Boot Test又定義一份重復的注解,目的在於使MockBean
和SpyBean
被ApplicationContext管理,從而方便使用。
MockBean和SpyBean功能非常相似,都能模擬方法的各種行為。不同之處在於MockBean是全新的對象,跟正式對象沒有關系;而SpyBean與正式對象緊密聯系,可以模擬正式對象的部分方法,沒有被模擬的方法仍然可以運行正式代碼。
(3) 自動配置類型的注解(@AutoConfigure*)
- @AutoConfigureJdbc 自動配置JDBC
- @AutoConfigureCache 自動配置緩存
- @AutoConfigureDataLdap 自動配置LDAP
- @AutoConfigureJson 自動配置JSON
- @AutoConfigureJsonTesters 自動配置JsonTester
- @AutoConfigureDataJpa 自動配置JPA
- @AutoConfigureTestEntityManager 自動配置TestEntityManager
- @AutoConfigureRestDocs 自動配置Rest Docs
- @AutoConfigureMockRestServiceServer 自動配置 MockRestServiceServer
- @AutoConfigureWebClient 自動配置 WebClient
- @AutoConfigureWebFlux 自動配置 WebFlux
- @AutoConfigureWebTestClient 自動配置 WebTestClient
- @AutoConfigureMockMvc 自動配置 MockMvc
- @AutoConfigureWebMvc 自動配置WebMvc
- @AutoConfigureDataNeo4j 自動配置 Neo4j
- @AutoConfigureDataRedis 自動配置 Redis
- @AutoConfigureJooq 自動配置 Jooq
- @AutoConfigureTestDatabase 自動配置Test Database,可以使用內存數據庫
這些注解可以搭配@\*Test
使用,用於開啟在@\*Test
中未自動配置的功能。例如@SpringBootTest
和@AutoConfigureMockMvc
組合后,就可以注入org.springframework.test.web.servlet.MockMvc
。
自動配置類型有兩種方式:
- 功能測試(即使用
@SpringBootTest
)時顯示添加。 - 一般在切片測試中被隱式使用,例如
@WebMvcTest
注解時,隱式添加了@AutoConfigureCache
、@AutoConfigureWebMvc
、@AutoConfigureMockMvc
。
(4) 啟動測試類型的注解(@*Test)
所有的@*Test注解都被@BootstrapWith注解,它們可以啟動ApplicationContext,是測試的入口,所有的測試類必須聲明一個@*Test注解。
- @SpringBootTest 自動偵測並加載@SpringBootApplication或@SpringBootConfiguration中的配置,默認web環境為MOCK,不監聽任務端口
- @DataRedisTest 測試對Redis操作,自動掃描被@RedisHash描述的類,並配置Spring Data Redis的庫
- @DataJpaTest 測試基於JPA的數據庫操作,同時提供了TestEntityManager替代JPA的EntityManager
- @DataJdbcTest 測試基於Spring Data JDBC的數據庫操作
- @JsonTest 測試JSON的序列化和反序列化
- @WebMvcTest 測試Spring MVC中的controllers
- @WebFluxTest 測試Spring WebFlux中的controllers
- @RestClientTest 測試對REST客戶端的操作
- @DataLdapTest 測試對LDAP的操作
- @DataMongoTest 測試對MongoDB的操作
- @DataNeo4jTest 測試對Neo4j的操作
除了@SpringBootTest
之外的注解都是用來進行切面測試的,他們會默認導入一些自動配。
一般情況下,推薦使用@SpringBootTest
而非其它切片測試的注解,簡單有效。若某次改動僅涉及特定切片,可以考慮使用切片測試。
@SpringBootTest
是這些注解中最常用的一個,其中包含的配置項如下:
- value 指定配置屬性
- properties 指定配置屬性,和value意義相同
- classes 指定配置類,等同於@ContextConfiguration中的class,若沒有顯示指定,將查找嵌套的@Configuration類,然后返回到SpringBootConfiguration搜索配置
- webEnvironment 指定web環境,可選值有:MOCK、RANDOM_PORT、DEFINED_PORT、NONE
webEnvironment
詳細說明:
- MOCK 此值為默認值,該類型提供一個mock環境,此時內嵌的服務(servlet容器)並沒有真正啟動,也不會監聽web端口。
- RANDOM_PORT 啟動一個真實的web服務,監聽一個隨機端口。
- DEFINED_PORT 啟動一個真實的web服務,監聽一個定義好的端口(從配置中讀取)。
- NONE 啟動一個非web的ApplicationContext,既不提供mock環境,也不提供真是的web服務。
2. 相互之間的搭配組合
package sample.test; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import sample.test.domain.VehicleIdentificationNumber; import sample.test.service.VehicleDetails; import sample.test.service.VehicleDetailsService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.test.context.junit4.SpringRunner; import static org.mockito.BDDMockito.given; @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) @AutoConfigureTestDatabase public class SampleTestApplicationWebIntegrationTests { private static final VehicleIdentificationNumber VIN = new VehicleIdentificationNumber( "01234567890123456"); @Autowired private TestRestTemplate restTemplate; @MockBean private VehicleDetailsService vehicleDetailsService; @Before public void setup() { given(this.vehicleDetailsService.getVehicleDetails(VIN)) .willReturn(new VehicleDetails("Honda", "Civic")); } @Test public void test() { this.restTemplate.getForEntity("/{username}/vehicle", String.class, "sframework"); } }
- @RunWith(SpringRunner.class)是JUnit的注解,作用是關聯Spring Boot Test,使運行JUnit時同時啟動Spring
- @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) 作用是啟動Spring的ApplicationContext,參數webEnvironment指定了運行的web環境
- @AutoConfigureTestDatabase 作用是啟動一個內存數據庫,不使用真實的數據庫
其中@RunWith和@*Test必須存在,@AutoConfigure*可以同時配置任意多個,而配置類型的注解可以在需要時添加。
3. 相似注解的區別於聯系
(1) @TestComment vs @Comment
@TestComponent
是另一種@Component
,在語義上用來指定某個Bean是專門用於測試的- 使用@SpringBootApplication服務時,
@TestComponent
會被自動排除
(2) @TestConfiguration vs @Configuration
@TestConfiguration
是Spring Boot Boot Test提供的,@Configuration
是Spring Framework提供的。@TestConfiguration
實際上是也是一種@TestComponent
,只是這個@TestComponent
專門用來做配置用。@TestConfiguration
和@Configuration
不同,它不會阻止@SpringBootTest
的查找機制,相當於是對既有配置的補充或覆蓋。
(3) @SpringBootTest vs @WebMvcTest(或@*Test)
- 都可以啟動Spring的ApplicationContext
- @SpringBootTest自動偵測並加載@SpringBootApplication或@SpringBootConfiguration中的配置,@WebMvcTest不偵測配置,只是默認加載一些自動配置。
- @SpringBootTest測試范圍一般比@WebMvcTest大。
轉自:沉迷思考的魚 http://ypk1226.com/2018/11/20/spring-boot/spring-boot-test-2/