springboot-單元測試梳理
今天主要是梳理springboot單元測試,基於MockMvc進行實戰演示。
文章目錄
1. sringboot的測試類庫
springboot本身就集成並且提供了非常多的實用工具以及註解。幫助開發者更加專注業務開發。
在這裏主要介紹一下,springboot提供的測試方面的工具以及註解。主要包括兩個模塊:
-
spring-boot-test: 支持測試的核心內容。
-
spring-boot-test-autoconfigure:支持測試的自動化配置
我們只需要引入上面的依賴,springboot就會幫我們引入測試模塊。我們只需要使用註解。就能輕鬆完成調用。
比如引入的測試類庫有:
- Junit: java單元測試的標準類庫。
- Spring Test & Spring Boot Test: Springboot功能集成化測試支持。
- AssertJ: 一個輕量級的斷言類庫
- Hamcrest: 一個對象匹配器類庫
- Mockito: 一個java Mock測試框架
2. Junit 4常用註解
-
@BeforeClass: 針對所有的測試,只執行一次,且方法還必須是static void
-
@Before: 初始化方法,執行當前測試類的每個測試方法前執行
-
@Test: 測試方法,在這裏執行測試,也可以測試期望異常和超時時間(可以查看註解源碼,查看使用屬性)
-
@After: 釋放資源,可以執行當前測試類的每一個測試方法後執行
-
@AfterClass: 針對所有的測試,只執行一次,且方法還必須是static void
-
@Ignore: 忽略此測試方法
-
@Runwith: 可以更改測試運行器
一個單元測試類執行的順序:
@BeforeClass
->@Before
->@Test
->@After
->@AfterClass
每個一個測試方法執行的調用順序:
@Before
->@Test
->@After
3. @RunWith @SpringBootTest
3.1 @RunWith
在JUnit中有很多個Runner,他們負責調用你的測試代碼,每一個Runner都有各自的特殊功能,你要根據需要選擇不同的Runner來運行你的測試代碼。在測試的時候,不指定使用的是默認的Runner。只能測試普通的java測試。不涉及spring web
項目
在springboot中,SpringBoot 2.X 默認使用Junit4 我們使用註解@RunWith(SpringRunner.class)
進行指定
public final class SpringRunner extends SpringJUnit4ClassRunner {
public SpringRunner(Class<?> clazz) throws InitializationError {
super(clazz);
}
}
可以看到源碼中,SpringRunner
繼承的SpringJUnit4ClassRunner
3.2 @SpringBootTest
@SpringBootTest註解是SpringBoot自1.4.0版本開始引入的一個用於測試的註解。他是屬於SpringBoot的註解。
在進行web 環境測試的時候,我通常使用@SpringBootTest(classes = SpringbootApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
其中classes = SpringbootApplication.class
是指定啓動類、webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT
是指定web環境
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@BootstrapWith(SpringBootTestContextBootstrapper.class)
public @interface SpringBootTest {
@AliasFor("properties")
String[] value() default {};
@AliasFor("value")
String[] properties() default {};
Class<?>[] classes() default {};
WebEnvironment webEnvironment() default WebEnvironment.MOCK;
enum WebEnvironment {
MOCK(false),
RANDOM_PORT(true),
DEFINED_PORT(true),
NONE(false);
private final boolean embedded;
WebEnvironment(boolean embedded) {
this.embedded = embedded;
}
public boolean isEmbedded() {
return this.embedded;
}
}
}
查看源碼發現:
SpringBootTest.WebEnvironment
中一共有 4 個枚舉值
- MOCK: MOCK 是用的比較多的一個屬性。它會判斷,當你使用 servlet API 時,則使用模擬 servlet 環境創建 WebApplicationContext;如果是使用 WebFlux,則使用 ReactiveWebApplicationContext;否則,使用常規的 ApplicationContext。
- RANDOM_PORT: 創建一個 web 應用程序上下文,可以是 reactive,也可以是 servlet。它將隨機起一個端口並監聽。到底是 reactive 還是 Servlet,它會自己根據上下文來判斷
- DEFINED_PORT: 創建一個(reactive) web 應用程序上下文,使用默認端口。針對這個屬性,網上翻譯的解讀比較多,錯誤的也多。
- NONE: 它不會指定 SpringApplication.setWebApplicationType。以非 Web 環境來運行。也就是非 Servlet 和 Reactive 環境
還有不少人的文章說,SpringBootTest.WebEnvironment.RANDOM_PORT 必須或通常和 @LocalServerPort 一起使用。我看源碼的註釋,解釋並沒有說跟@LocalServerPort 一起使用。我本身也不用。
爲了減少篇幅,文中源碼註釋我都做了刪除處理。感興趣可以自己去查看。
4. MockMVC介紹
MockMvc是由spring-test包提供,實現了對Http請求的模擬,能夠直接使用網絡的形勢,轉換到Controller的調用,是的測試速度快,不依賴網絡環境。同時提供了一套驗證的工具,結果的驗證十分方便。
基於RESTful 風格的springMVC測試。可以測試整個SpringMVC流程。從URL請求到Controller控制層的測試。
4.1 MockMvc
- 作用: 服務器端SpringMVC測試的主入口點
- 創建: 通過
MockMVCBuilders
的靜態方法建造MockMVCBuilder
,MockMvc
由MockMVCBuilder
構造 - 調用: perform(RequestBuilder rb),執行一個RequestBuilder請求,會自動執行SpringMVC的流程並映射到相應的控制器執行處理,該方法的返回值是一個ResultActions。
4.2 MockMVCBuilders
- 作用: 負責創建MockMVCBuilder對象
- 創建: 有兩種創建方式
- standaloneSetup(Object… controllers): 通過參數指定一組控制器,這樣就不需要從上下文獲取了。
- webAppContextSetup(WebApplicationContext wac):指定WebApplicationContext,將會從該上下文獲取相應的控制器並得到相應的MockMv
4.3 MockMVCBuilder
- 作用: MockMVCBuilder使用構造者模式來構建MockMvc的構造器
- 創建: 主要的實現:StandaloneMockMvcBuilder和DefaultMockMvcBuilder
- 也可以直接使用靜態工廠MockMvcBuilders創建即可,不需要直接使用上面兩個實現類
4.4 MockMvcRequestBuilders
- 作用: 用來構建Request請求的。
- 創建: 其主要有兩個子類MockHttpServletRequestBuilder和MockMultipartHttpServletRequestBuilder(如文件上傳使用),即用來Mock客戶端請求需要的所有數據。
4.5 ResultActions
方法介紹:
- andExpect:添加ResultMatcher驗證規則,驗證控制器執行完成後結果是否正確。
- andDo:添加ResultHandler結果處理器,比如調試時打印結果到控制檯。
- andReturn:最後返回相應的MvcResult;然後進行自定義驗證/進行下一步的異步處理。
- MockMvcResultMatchers
- 用來匹配執行完請求後的**結果驗證。
- 果匹配失敗將拋出相應的異常。
- 包含了很多驗證API方法。
- MockMvcResultHandlers
- 結果處理器,表示要對結果做點什麼事情。
- 比如此處使用MockMvcResultHandlers.print()輸出整個響應結果信息。
4.6 MvcResult
- 作用: 單元測試執行結果,可以針對執行結果進行自定義驗證邏輯。採用斷言驗證數據。
5. 實戰
完整的代碼github路徑: [email protected]:agreewangshuai/marsha_springboot.git
(一個完整的springboot骨架集成統一結果返回、統一異常處理、統一日誌框架以及單元測試、swagger)
添加Gradle依賴
testImplementation('org.springframework.boot:spring-boot-starter-test')
model
@ApiModel(value = "HelloWorld參數類")
public class HelloWorld {
@ApiModelProperty(value = "要說內容")
@NotNull(message = "不能爲空或者\"\"")
private String say;
public String getSay() {
return say;
}
public void setSay(String say) {
this.say = say;
}
}
controller
@RestController
@RequestMapping("/agree")
@Api(value = "類描述",tags = {"hello demo功能"})
public class HelloWorldController {
@Autowired
HelloWorldService helloWorldService;
@ApiOperation(value = "返回say hello",notes = "方法的具體描述信息")
@GetMapping("/helloworld")
public ResponseData getHello(@RequestBody @Valid HelloWorld helloWorld) {
String say = helloWorld.getSay();
String sayhello = helloWorldService.sayhello(say);
return new ResponseData().ok().data(sayhello);
}
}
service
public interface HelloWorldService {
/**
* @param say
* @return
*/
String sayhello(String say);
}
serviceImpl
@Service
public class HelloWorldServiceImpl implements HelloWorldService{
@Override
public String sayhello(String say) {
return say;
}
}
Test
@RunWith(SpringRunner.class)
@SpringBootTest(classes = SpringbootApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class HelloWorldControllerTest {
public static final Logger log = LoggerFactory.getLogger(HelloWorldControllerTest.class);
@Autowired
private WebApplicationContext ctx;
private MockMvc mvc;
@Before
public void before() {
mvc = MockMvcBuilders.webAppContextSetup(ctx).build();
}
@Test
public void getHelloTest() throws Exception {
RequestBuilder request = null;
MvcResult mvcResult = null;
HelloWorld helloWorld = new HelloWorld();
// helloWorld.setSay("say agree");
/**
* 添加請求參數,此處示例傳參格式爲json
* new JsonHelper().objectToJson(helloWorld)是我自己寫的一個util類,將model轉爲json
* 嫌麻煩也可以直接寫成json格式:
* content("{\"say\":\"hello world\"}")
*/
request = MockMvcRequestBuilders.get("/agree/helloworld").content(new JsonHelper().objectToJson(helloWorld))
.contentType(MediaType.APPLICATION_JSON);
mvcResult = mvc.perform(request).andExpect(status().isOk()).andReturn();
// 獲取數據
JSONObject jsonObject = new JSONObject(mvcResult.getResponse().getContentAsString());
log.info("輸出返回結果:{}", jsonObject);
// 加斷言,判斷屬性值的問題。
Assert.assertNotNull(jsonObject.get("code"));
Assert.assertEquals(jsonObject.get("code"), "0");
Assert.assertNotNull(jsonObject.get("message"));
Assert.assertEquals(jsonObject.get("message"), "操作成功");
Assert.assertNotNull(jsonObject.get("data"));
}
}
小結:
本文單元測試主要描述了springboot單元測試的MockMvc。主要針對controller層的單元測試方法的介紹。目前暫時用的功能就這麼多。後續有新的研究心得,會持續更新。如有紕漏,歡迎指正。謝謝