使用SpringBoot如何模拟请求只对Web层进行测试?我们可以使用Mockito框架来模拟我们需要的对象,但是模拟一个response显然很复杂。对此,SpringBoot有更好的测试方式——通过MockMvc。
@Controller
public class HomeController {
@RequestMapping("/")
public @ResponseBody String greeting() {
return "Hello, World";
}
}
@SpringBootApplication
public class TestingWebApplication {
public static void main(String[] args) {
SpringApplication.run(TestingWebApplication.class, args);
}
}
上面是一个简单的SpringApplication,启动项目之后,我们可以输入网址http://localhost:8080
来检查项目的Spring Context是否启动成功。
@SpringBootTest
public class SmokeTest {
@Autowired
private HomeController controller;
@Test
public void contextLoads() throws Exception {
assertThat(controller).isNotNull();
}
}
@SpringBootTest
注解会去找带有@SpringBootApplication
注解的类,用它来启动Spring Application的环境。controller不为空说明HomeController被成功注入。@Autowired
注解的bean 在测试方法运行前被注入。作者:当当一丢丢 链接:https://www.jianshu.com/p/5dae923e64a8 来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
上面的test可以测试Spring是否正常启动,如果我们要测试程序的行为,我们还应该要模拟HTTP请求,并对response进行断言。看下面的测试,被测试类同上一个例子相同:
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class HttpRequestTest {
@LocalServerPort
private int port;
@Autowired
private TestRestTemplate restTemplate;
@Test
public void greetingShouldReturnDefaultMessage() throws Exception {
assertThat(this.restTemplate.getForObject("http://localhost:" + port + "/",
String.class)).contains("Hello, World");
}
}
webEnvironment = WebEnvironment.RANDOM_PORT
就会产生一个随机端口号(防止端口冲突)。@LocalServerPort
注解将这个端口号注入port中。@TestRestTemlpate
Spring boot 提供一个TestRestTemplate
,作为 Http Client。上面的测试,每次运行都要启动Spring服务器,显然很麻烦。用Spring的MockMvc来发送请求,就可以启动Full Spring Context而无需启动服务器。下面看例子:
@SpringBootTest
@AutoConfigureMockMvc
public class TestingWebApplicationTest {
@Autowired
private MockMvc mockMvc;
@Test
public void shouldReturnDefaultMessage() throws Exception {
this.mockMvc.perform(get("/")).andDo(print()).andExpect(status().isOk())
.andExpect(content().string(containsString("Hello, World")));
}
}
@AutoConfigureMockMvc
注解将mockMvc自动注入。在上面的测试中,将启动Full Spring Context,但不启动服务器。我们可以通过使用@WebMvcTest
将测试范围缩小到web层,看下面的测试:
@WebMvcTest
public class WebLayerTest {
@Autowired
private MockMvc mockMvc;
@Test
public void shouldReturnDefaultMessage() throws Exception {
this.mockMvc.perform(get("/")).andDo(print()).andExpect(status().isOk())
.andExpect(content().string(containsString("Hello, World")));
}
}
@WebMvcTest(SthController.class)
指明具体限制注入的controller 对象,未加 class 属性的时候注入所有 controller。有了上面的学习,我们可以试着写更复杂一点的Spring测试。
@Controller
public class GreetingController {
private final GreetingService service;
public GreetingController(GreetingService service) {
this.service = service;
}
@RequestMapping("/greeting")
public @ResponseBody String greeting() {
return service.greet();
}
}
@Service
public class GreetingService {
public String greet() {
return "Hello, World";
}
}
@WebMvcTest(GreetingController.class)
public class WebMockTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private GreetingService service;
@Test
public void greetingShouldReturnMessageFromService() throws Exception {
when(service.greet()).thenReturn("Hello, Mock");
this.mockMvc.perform(get("/greeting")).andDo(print()).andExpect(status().isOk())
.andExpect(content().string(containsString("Hello, Mock")));
}
}
@MockBean
为GreetingService
创建并注入一个mock(如果不这样做,应用程序上下文将无法启动),并使用Mockito设置其期望值。@WebMvcTest
就要将其他层的对象使用@MockBean
注入,否则项目无法启动。@SpringBootTest
和@AutoConfigureMockMvc
则不需要,因为它是full stack的启动。参考:
SpringBoot 测试-Web层
spring-test - Testing the Web Layer