如何測試Spring MVC應用
Spring的依賴注入使得我們的代碼非常容易進行單元測試——@Controller, @Service,@Entity等注解標注的類基本都是POJO(plain old Java object),也就是說很少依賴于Spring容器本身的API。我們可以非常容易地使用JUnit或TestNG編寫測試代碼。另一方面,對于三層架構的Spring Web應用(Controller, Service, DAO),使用Mock活Stub方法也能夠更好的來測試我們的代碼邏輯。例如Service層代碼的單元測試中,依賴的DAO(或Repository)對象都是根據(jù)應用測試需求Mock出來的,而不需要真正去訪問數(shù)據(jù)庫。
Spring Web測試
在對Spring Web應用中的@Controller代碼進行單元測試的過程中,一般的方法是創(chuàng)建@Controller對象,同時將它依賴的一些Mock對象——例如MockHttpServletRequest, MockHttpServletResponse(都由spring-test模塊提供,無需自己編寫)作為@Controller方法的參數(shù)。但是對于處理Web請求的@Controller代碼來說,僅僅測試Handler方法里的代碼是遠遠不夠的,對于一個處理HTTP請求的@Controller`,我們還需要測試:
@RequestMapping路由是否正確- 數(shù)據(jù)綁定、類型轉換、校驗邏輯是否正確——數(shù)據(jù)包括URL參數(shù)、表單、
@PathVariable等 @InitBinder,@ModelAttribute,@ExceptionHandler等注解的方法或?qū)傩杂嬎氵^程
上述過程貫穿于HTTP請求處理的生命周期中,所以對于Spring Web應用中@Controller代碼單元測試的概念,應該做一些擴充——不僅僅局限于代碼本身,也要結合MVC框架中的各個處理過程。
本文接下來的內(nèi)容代碼,都以Spring Boot為例,首先假設我們通過Spring Boot創(chuàng)建了一個最簡單的Web Mvc應用——包含了一個最簡單的Conroller,處理/users/{id}對應的HTTP請求,返回值是id={id}(通過String.format()方法),那么可以為它創(chuàng)建如下測試代碼:
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = SpringMvcTestDemoApplication.class)
@WebAppConfiguration
public class SpringMvcTestDemoApplicationTests {
private MockMvc mockMvc;
@Before
public void init() {
this.mockMvc = MockMvcBuilders.standaloneSetup(new UserController()).build();
}
@Test
public void getUserById() throws Exception {
long id = 1;
this.mockMvc.perform(get("/users/" + id))
.andExpect(status().isOk())
.andExpect(content().string("id=" + id));
}
}
運行上述測試時,很容易從控制臺中的日志發(fā)現(xiàn),SpringJUnit4ClassRunner創(chuàng)建了一個Spring Web應用上下文,并且在其中進行了Web Mvc框架的配置——這里是注冊@RequestMapping方法。接下來mockMvc.perform()方法實際上向該Spring Web應用發(fā)起了一個HTTP請求:
- 請求的url為
/users/{id} andExpect()方法也就是測試中常用的Assertstatus()用于檢查返回狀態(tài)嗎,這里是200content()用于檢查內(nèi)容
如果我們不小心將@RequestMapping的路由路徑寫錯,那么這里運行的結果一定不會是status().isOk(),這也就完成了對HTTP請求路由的測試。接下來我們將繼續(xù)探索MVC框架中的其他方面。
Mock Service
在Spring Web應用三層結構里,Controller層代碼通常會調(diào)用Service層代碼,例如:
@RestController
public class UserController {
@Autowired
private UserService userService;
@RequestMapping(value = "/users/{id}", method = GET)
public String get(@PathVariable("id") long id) {
String username = userService.getUsername(id);
return String.format("username=%s", username);
}
}
對UserController進行單元測試需要排除Service代碼的影響,所以需要對Service進行Mock,這里我們使用Mockito框架,在Spring上下文中Mock一個UserService對象:
@Configuration
public class TestContext {
@Bean
public UserService userServiceMock() {
return Mockito.mock(UserService.class);
}
}
同時通過Mockito的API來MockUserService.getUsername(long id)方法,@Controller的測試代碼如下:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = {
SpringMvcTestDemoApplication.class,
TestContext.class
})
@WebAppConfiguration
public class SpringMvcTestDemoApplicationTests {
@Autowired
UserService userService;
@Autowired
UserController controller;
MockMvc mockMvc;
@Before
public void init() {
this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
@Test
public void getUserById() throws Exception {
long id = 1L;
String ricky = "Ricky";
Mockito.when(userService.getUsername(id)).thenReturn(ricky);
this.mockMvc.perform(get("/users/" + id))
.andExpect(status().isOk())
.andExpect(content().string("username=" + ricky));
}
}
由于需要進行依賴注入,所以UserService和UserController都使用@Autowired注解。Mockito.when(userService.getUsername(id)).thenReturn(ricky);表明userService.getUsername()方法的參數(shù)為1L時,返回值為"Ricky",Mockito提供能很多強大的Mock API,更多用法請參考官方文檔。
測試REST API
當我們構建REST服務時,大多數(shù)情況會使用JSON作為數(shù)據(jù)交換格式,Spring MVC測試框架同樣提供了一種簡潔的方式對JSON結果進行斷言,假設現(xiàn)在有@Controller如下:
@RequestMapping(value = "/users/{id}/json", method = GET)
public User getUser(@PathVariable("id") long id) {
String username = userService.getUsername(id);
return new User(id, username);
}
static class User {
public long id;
public String username;
//構造方法,Getter/Setter略
}
實際應用返回的JSON數(shù)據(jù)是:
{
"id": 1,
"username": "Ricky"
}
測試代碼可以這樣斷言:
@Test
public void getUser() throws Exception {
this.mockMvc.perform(get("/users/{id}/json", id).accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(id.intValue()))
.andExpect(jsonPath("$.username").value(ricky));
}
$.id, $.username都是JsonPath提供的JSON表達式,可以通過jsonPath、value()等方法來輕松對JSON數(shù)據(jù)進行斷言而不需要自己編寫JSON文本處理。
以上就是如何測試Spring MVC應用的詳細內(nèi)容,更多關于測試Spring MVC應用的資料請關注腳本之家其它相關文章!
相關文章
SpringBoot使用jasypt加解密密碼的實現(xiàn)方法
這篇文章主要介紹了SpringBoot使用jasypt加解密密碼的實現(xiàn)方法,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-10-10

